From 0254a75c95fc2ee01b736cd54ae40c14087d1dd7 Mon Sep 17 00:00:00 2001 From: Guillaume Lajarige Date: Wed, 31 Jan 2018 13:37:51 +0000 Subject: [PATCH] =?UTF-8?q?N=C2=B01280=20Upgrade=20Silex=20library=20to=20?= =?UTF-8?q?2.2=20(Which=20is=20possible=20as=20iTop=202.5=20requirements?= =?UTF-8?q?=20are=20now=20PHP=205.6+!)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit SVN:trunk[5305] --- .../src/forms/objectformmanager.class.inc.php | 4 +- .../helpers/applicationhelper.class.inc.php | 20 +- ...xtmanipulatorserviceprovider.class.inc.php | 12 +- ...yclevalidatorserviceprovider.class.inc.php | 12 +- ...copevalidatorserviceprovider.class.inc.php | 12 +- .../urlgeneratorserviceprovider.class.inc.php | 12 +- lib/silex/composer.json | 4 +- lib/silex/composer.lock | 578 ++-- lib/silex/vendor/autoload.php | 4 +- lib/silex/vendor/composer/ClassLoader.php | 68 +- lib/silex/vendor/composer/LICENSE | 2 +- .../vendor/composer/autoload_classmap.php | 8 +- lib/silex/vendor/composer/autoload_files.php | 12 + .../vendor/composer/autoload_namespaces.php | 9 +- lib/silex/vendor/composer/autoload_psr4.php | 11 + lib/silex/vendor/composer/autoload_real.php | 52 +- lib/silex/vendor/composer/autoload_static.php | 122 + lib/silex/vendor/composer/installed.json | 1145 ++++---- .../vendor/paragonie/random_compat/LICENSE | 22 + .../paragonie/random_compat/build-phar.sh | 5 + .../paragonie/random_compat/composer.json | 37 + .../dist/random_compat.phar.pubkey | 5 + .../dist/random_compat.phar.pubkey.asc | 11 + .../random_compat/lib/byte_safe_strings.php | 181 ++ .../random_compat/lib/cast_to_int.php | 75 + .../random_compat/lib/error_polyfill.php | 49 + .../paragonie/random_compat/lib/random.php | 223 ++ .../lib/random_bytes_com_dotnet.php | 88 + .../lib/random_bytes_dev_urandom.php | 167 ++ .../lib/random_bytes_libsodium.php | 88 + .../lib/random_bytes_libsodium_legacy.php | 92 + .../random_compat/lib/random_bytes_mcrypt.php | 77 + .../random_compat/lib/random_int.php | 190 ++ .../random_compat/other/build_phar.php | 57 + .../random_compat/psalm-autoload.php | 9 + .../vendor/paragonie/random_compat/psalm.xml | 16 + lib/silex/vendor/pimple/pimple/.gitignore | 2 + lib/silex/vendor/pimple/pimple/.travis.yml | 40 + lib/silex/vendor/pimple/pimple/CHANGELOG | 59 + lib/silex/vendor/pimple/pimple/LICENSE | 2 +- lib/silex/vendor/pimple/pimple/README.rst | 343 ++- lib/silex/vendor/pimple/pimple/composer.json | 14 +- .../pimple/pimple/ext/pimple/.gitignore | 30 + .../vendor/pimple/pimple/ext/pimple/README.md | 12 + .../vendor/pimple/pimple/ext/pimple/config.m4 | 63 + .../pimple/pimple/ext/pimple/config.w32 | 13 + .../pimple/pimple/ext/pimple/php_pimple.h | 137 + .../vendor/pimple/pimple/ext/pimple/pimple.c | 1114 ++++++++ .../pimple/pimple/ext/pimple/pimple_compat.h | 81 + .../pimple/pimple/ext/pimple/tests/001.phpt | 45 + .../pimple/pimple/ext/pimple/tests/002.phpt | 15 + .../pimple/pimple/ext/pimple/tests/003.phpt | 16 + .../pimple/pimple/ext/pimple/tests/004.phpt | 30 + .../pimple/pimple/ext/pimple/tests/005.phpt | 27 + .../pimple/pimple/ext/pimple/tests/006.phpt | 51 + .../pimple/pimple/ext/pimple/tests/007.phpt | 22 + .../pimple/pimple/ext/pimple/tests/008.phpt | 29 + .../pimple/pimple/ext/pimple/tests/009.phpt | 13 + .../pimple/pimple/ext/pimple/tests/010.phpt | 45 + .../pimple/pimple/ext/pimple/tests/011.phpt | 19 + .../pimple/pimple/ext/pimple/tests/012.phpt | 28 + .../pimple/pimple/ext/pimple/tests/013.phpt | 33 + .../pimple/pimple/ext/pimple/tests/014.phpt | 30 + .../pimple/pimple/ext/pimple/tests/015.phpt | 17 + .../pimple/pimple/ext/pimple/tests/016.phpt | 24 + .../pimple/pimple/ext/pimple/tests/017.phpt | 17 + .../pimple/pimple/ext/pimple/tests/017_1.phpt | 17 + .../pimple/pimple/ext/pimple/tests/018.phpt | 23 + .../pimple/pimple/ext/pimple/tests/019.phpt | 18 + .../pimple/pimple/ext/pimple/tests/bench.phpb | 51 + .../pimple/ext/pimple/tests/bench_shared.phpb | 25 + .../vendor/pimple/pimple/phpunit.xml.dist | 14 + .../pimple/pimple/src/Pimple/Container.php | 298 +++ .../Exception/ExpectedInvokableException.php | 38 + .../Exception/FrozenServiceException.php | 45 + .../InvalidServiceIdentifierException.php | 45 + .../Exception/UnknownIdentifierException.php | 45 + .../pimple/src/Pimple/Psr11/Container.php | 55 + .../src/Pimple/Psr11/ServiceLocator.php | 75 + .../pimple/src/Pimple/ServiceIterator.php | 69 + .../src/Pimple/ServiceProviderInterface.php | 46 + .../src/Pimple/Tests/Fixtures/Invokable.php | 38 + .../Pimple/Tests/Fixtures/NonInvokable.php | 34 + .../Tests/Fixtures/PimpleServiceProvider.php | 54 + .../src/Pimple/Tests/Fixtures/Service.php | 35 + .../PimpleServiceProviderInterfaceTest.php | 76 + .../pimple/src/Pimple/Tests/PimpleTest.php | 589 +++++ .../src/Pimple/Tests/Psr11/ContainerTest.php | 77 + .../Pimple/Tests/Psr11/ServiceLocatorTest.php | 134 + .../src/Pimple/Tests/ServiceIteratorTest.php | 52 + lib/silex/vendor/psr/container/.gitignore | 3 + lib/silex/vendor/psr/container/LICENSE | 21 + lib/silex/vendor/psr/container/README.md | 5 + lib/silex/vendor/psr/container/composer.json | 27 + .../src/ContainerExceptionInterface.php | 13 + .../psr/container/src/ContainerInterface.php | 37 + .../src/NotFoundExceptionInterface.php | 13 + .../vendor/psr/log/Psr/Log/AbstractLogger.php | 40 +- lib/silex/vendor/psr/log/Psr/Log/LogLevel.php | 16 +- .../psr/log/Psr/Log/LoggerAwareInterface.php | 7 +- .../psr/log/Psr/Log/LoggerAwareTrait.php | 8 +- .../psr/log/Psr/Log/LoggerInterface.php | 51 +- .../vendor/psr/log/Psr/Log/LoggerTrait.php | 51 +- .../vendor/psr/log/Psr/Log/NullLogger.php | 9 +- .../log/Psr/Log/Test/LoggerInterfaceTest.php | 46 +- lib/silex/vendor/psr/log/composer.json | 13 +- lib/silex/vendor/silex/silex/.travis.yml | 60 + lib/silex/vendor/silex/silex/LICENSE | 2 +- lib/silex/vendor/silex/silex/README.rst | 23 +- lib/silex/vendor/silex/silex/composer.json | 80 +- .../vendor/silex/silex/doc/changelog.rst | 139 + .../vendor/silex/silex/doc/contributing.rst | 39 +- .../silex/doc/cookbook/error_handler.rst | 25 +- .../silex/silex/doc/cookbook/form_no_csrf.rst | 13 +- .../doc/cookbook/guard_authentication.rst | 183 ++ .../vendor/silex/silex/doc/cookbook/index.rst | 7 +- .../silex/doc/cookbook/multiple_loggers.rst | 18 +- .../silex/doc/cookbook/session_storage.rst | 41 +- .../silex/silex/doc/cookbook/sub_requests.rst | 50 +- .../silex/doc/cookbook/validator_yaml.rst | 2 +- lib/silex/vendor/silex/silex/doc/index.rst | 5 +- .../vendor/silex/silex/doc/internals.rst | 17 +- lib/silex/vendor/silex/silex/doc/intro.rst | 39 +- .../vendor/silex/silex/doc/middlewares.rst | 47 +- .../silex/doc/organizing_controllers.rst | 13 +- .../vendor/silex/silex/doc/providers.rst | 110 +- .../silex/silex/doc/providers/asset.rst | 69 + .../vendor/silex/silex/doc/providers/csrf.rst | 53 + .../silex/silex/doc/providers/doctrine.rst | 14 +- .../vendor/silex/silex/doc/providers/form.rst | 112 +- .../silex/silex/doc/providers/http_cache.rst | 20 +- .../silex/doc/providers/http_fragment.rst | 10 +- .../silex/silex/doc/providers/index.rst | 13 +- .../silex/silex/doc/providers/locale.rst | 24 + .../silex/silex/doc/providers/monolog.rst | 36 +- .../silex/silex/doc/providers/remember_me.rst | 4 +- .../silex/silex/doc/providers/security.rst | 163 +- .../silex/silex/doc/providers/serializer.rst | 33 +- .../doc/providers/service_controller.rst | 38 +- .../silex/silex/doc/providers/session.rst | 30 +- .../silex/silex/doc/providers/swiftmailer.rst | 35 +- .../silex/silex/doc/providers/translation.rst | 44 +- .../vendor/silex/silex/doc/providers/twig.rst | 182 +- .../silex/silex/doc/providers/validator.rst | 48 +- .../silex/silex/doc/providers/var_dumper.rst | 44 + lib/silex/vendor/silex/silex/doc/services.rst | 132 +- lib/silex/vendor/silex/silex/doc/testing.rst | 64 +- lib/silex/vendor/silex/silex/doc/usage.rst | 261 +- .../vendor/silex/silex/doc/web_servers.rst | 84 +- lib/silex/vendor/silex/silex/phpunit.xml.dist | 2 +- .../Silex/Api/BootableProviderInterface.php | 33 + .../Silex/Api/ControllerProviderInterface.php | 32 + .../Api/EventListenerProviderInterface.php | 25 + .../vendor/silex/silex/src/Silex/Api/LICENSE | 19 + .../silex/silex/src/Silex/Api/composer.json | 34 + .../src/Silex/AppArgumentValueResolver.php | 47 + .../silex/silex/src/Silex/Application.php | 201 +- .../silex/src/Silex/Application/FormTrait.php | 29 +- .../src/Silex/Application/SecurityTrait.php | 37 +- .../silex/src/Silex/CallbackResolver.php | 26 +- .../silex/silex/src/Silex/Controller.php | 10 +- .../silex/src/Silex/ControllerCollection.php | 70 +- .../silex/src/Silex/ControllerResolver.php | 2 + .../src/Silex/EventListener/LogListener.php | 42 +- .../EventListener/MiddlewareListener.php | 2 +- .../Exception/ControllerFrozenException.php | 2 +- .../silex/src/Silex/ExceptionHandler.php | 24 +- .../src/Silex/ExceptionListenerWrapper.php | 2 +- .../Silex/Provider/AssetServiceProvider.php | 97 + .../Silex/Provider/CsrfServiceProvider.php | 48 + .../Provider/DoctrineServiceProvider.php | 56 +- .../ExceptionHandlerServiceProvider.php | 32 + .../Provider/Form/SilexFormExtension.php | 122 + .../Silex/Provider/FormServiceProvider.php | 101 +- .../Silex/Provider/HttpCache/HttpCache.php | 39 + .../Provider/HttpCacheServiceProvider.php | 36 +- .../Provider/HttpFragmentServiceProvider.php | 53 +- .../Provider/HttpKernelServiceProvider.php | 101 + .../silex/silex/src/Silex/Provider/LICENSE | 19 + .../Silex/Provider/Locale/LocaleListener.php | 84 + .../Silex/Provider/LocaleServiceProvider.php | 40 + .../Silex/Provider/MonologServiceProvider.php | 78 +- .../Provider/RememberMeServiceProvider.php | 56 +- .../Provider/Routing/LazyRequestMatcher.php | 55 + .../Routing/RedirectableUrlMatcher.php | 55 + .../Silex/Provider/RoutingServiceProvider.php | 87 + .../Provider/SecurityServiceProvider.php | 375 ++- .../Provider/SerializerServiceProvider.php | 31 +- .../ServiceControllerServiceProvider.php | 15 +- .../Provider/Session/SessionListener.php | 39 + .../Provider/Session/TestSessionListener.php | 39 + .../Silex/Provider/SessionServiceProvider.php | 110 +- .../Provider/SwiftmailerServiceProvider.php | 97 +- .../Provider/TranslationServiceProvider.php | 67 +- .../src/Silex/Provider/Twig/RuntimeLoader.php | 41 + .../Silex/Provider/TwigServiceProvider.php | 145 +- .../Validator/ConstraintValidatorFactory.php | 63 + .../Provider/ValidatorServiceProvider.php | 61 +- .../Provider/VarDumperServiceProvider.php | 58 + .../silex/src/Silex/Provider/composer.json | 31 + .../vendor/silex/silex/src/Silex/Route.php | 14 + .../silex/src/Silex/Route/SecurityTrait.php | 2 +- .../silex/src/Silex/ViewListenerWrapper.php | 87 + .../silex/silex/src/Silex/WebTestCase.php | 19 +- .../Silex/Tests/Application/FormTraitTest.php | 16 +- .../Tests/Application/MonologTraitTest.php | 10 +- .../Tests/Application/SecurityTraitTest.php | 110 +- .../Application/SwiftmailerTraitTest.php | 5 +- .../Application/TranslationTraitTest.php | 5 +- .../Silex/Tests/Application/TwigTraitTest.php | 5 +- .../Application/UrlGeneratorTraitTest.php | 27 +- .../tests/Silex/Tests/ApplicationTest.php | 269 +- .../Silex/Tests/CallbackResolverTest.php | 55 +- .../Silex/Tests/CallbackServicesTest.php | 7 +- .../Silex/Tests/ControllerCollectionTest.php | 170 +- .../Silex/Tests/ControllerResolverTest.php | 11 +- .../tests/Silex/Tests/ControllerTest.php | 16 +- .../Tests/EventListener/LogListenerTest.php | 40 +- .../Silex/Tests/ExceptionHandlerTest.php | 44 +- .../Silex/Tests/Fixtures/Php7Controller.php | 22 + .../tests/Silex/Tests/Fixtures/manifest.json | 3 + .../tests/Silex/Tests/FunctionalTest.php | 3 +- .../silex/tests/Silex/Tests/JsonTest.php | 3 +- .../tests/Silex/Tests/LazyDispatcherTest.php | 9 +- .../Silex/Tests/LazyRequestMatcherTest.php | 78 + .../silex/tests/Silex/Tests/LocaleTest.php | 10 +- .../tests/Silex/Tests/MiddlewareTest.php | 35 +- .../Provider/AssetServiceProviderTest.php | 52 + .../Provider/DoctrineServiceProviderTest.php | 44 +- .../Provider/FormServiceProviderTest.php | 263 +- .../DisableCsrfExtension.php | 22 + .../Provider/HttpCacheServiceProviderTest.php | 5 +- .../HttpFragmentServiceProviderTest.php | 10 +- .../Provider/MonologServiceProviderTest.php | 87 +- .../RememberMeServiceProviderTest.php | 33 +- .../Provider/RoutingServiceProviderTest.php | 122 + .../Provider/SecurityServiceProviderTest.php | 224 +- .../TokenAuthenticator.php | 79 + .../SerializerServiceProviderTest.php | 3 +- .../Provider/SessionServiceProviderTest.php | 23 +- .../tests/Silex/Tests/Provider/SpoolStub.php | 2 +- .../SwiftmailerServiceProviderTest.php | 54 +- .../TranslationServiceProviderTest.php | 74 +- .../Provider/TwigServiceProviderTest.php | 150 +- .../Provider/ValidatorServiceProviderTest.php | 110 +- .../Silex/Tests/Route/SecurityTraitTest.php | 7 +- .../silex/tests/Silex/Tests/RouterTest.php | 26 +- .../ServiceControllerResolverRouterTest.php | 5 +- .../Tests/ServiceControllerResolverTest.php | 18 +- .../silex/tests/Silex/Tests/StreamTest.php | 7 +- .../tests/Silex/Tests/WebTestCaseTest.php | 9 +- lib/silex/vendor/symfony/debug/.gitignore | 3 + .../vendor/symfony/debug/BufferingLogger.php | 37 + lib/silex/vendor/symfony/debug/CHANGELOG.md | 64 + lib/silex/vendor/symfony/debug/Debug.php | 63 + .../vendor/symfony/debug/DebugClassLoader.php | 409 +++ .../vendor/symfony/debug/ErrorHandler.php | 758 ++++++ .../Exception/ClassNotFoundException.php | 33 + .../debug/Exception/ContextErrorException.php | 40 + .../debug/Exception/FatalErrorException.php | 82 + .../debug/Exception/FatalThrowableError.php | 44 + .../debug/Exception/FlattenException.php | 263 ++ .../debug/Exception/OutOfMemoryException.php | 21 + .../debug/Exception/SilencedErrorContext.php | 67 + .../Exception/UndefinedFunctionException.php | 33 + .../Exception/UndefinedMethodException.php | 33 + .../vendor/symfony/debug/ExceptionHandler.php | 410 +++ .../ClassNotFoundFatalErrorHandler.php | 206 ++ .../FatalErrorHandlerInterface.php | 32 + .../UndefinedFunctionFatalErrorHandler.php | 84 + .../UndefinedMethodFatalErrorHandler.php | 66 + lib/silex/vendor/symfony/debug/LICENSE | 19 + lib/silex/vendor/symfony/debug/README.md | 13 + .../symfony/debug/Resources/ext/README.md | 134 + .../symfony/debug/Resources/ext/config.m4 | 63 + .../symfony/debug/Resources/ext/config.w32 | 13 + .../debug/Resources/ext/php_symfony_debug.h | 60 + .../debug/Resources/ext/symfony_debug.c | 283 ++ .../debug/Resources/ext/tests/001.phpt | 155 ++ .../debug/Resources/ext/tests/002.phpt | 65 + .../debug/Resources/ext/tests/002_1.phpt | 48 + .../debug/Resources/ext/tests/003.phpt | 87 + .../debug/Tests/DebugClassLoaderTest.php | 425 +++ .../symfony/debug/Tests/ErrorHandlerTest.php | 548 ++++ .../Tests/Exception/FlattenExceptionTest.php | 302 +++ .../debug/Tests/ExceptionHandlerTest.php | 133 + .../ClassNotFoundFatalErrorHandlerTest.php | 176 ++ ...UndefinedFunctionFatalErrorHandlerTest.php | 81 + .../UndefinedMethodFatalErrorHandlerTest.php | 76 + .../debug/Tests/Fixtures/AnnotatedClass.php | 13 + .../debug/Tests/Fixtures/ClassAlias.php | 3 + .../debug/Tests/Fixtures/DeprecatedClass.php | 12 + .../Tests/Fixtures/DeprecatedInterface.php | 12 + .../Tests/Fixtures/ExtendedFinalMethod.php | 17 + .../debug/Tests/Fixtures/FinalClass.php | 10 + .../debug/Tests/Fixtures/FinalMethod.php | 17 + .../debug/Tests/Fixtures/InternalClass.php | 15 + .../Tests/Fixtures/InternalInterface.php | 10 + .../debug/Tests/Fixtures/InternalTrait.php | 10 + .../debug/Tests/Fixtures/InternalTrait2.php | 23 + .../Tests/Fixtures/NonDeprecatedInterface.php | 7 + .../debug/Tests/Fixtures/PEARClass.php | 5 + .../symfony/debug/Tests/Fixtures/Throwing.php | 3 + .../debug/Tests/Fixtures/ToStringThrower.php | 24 + .../debug/Tests/Fixtures/casemismatch.php | 7 + .../debug/Tests/Fixtures/notPsr0Bis.php | 7 + .../Tests/Fixtures/psr4/Psr4CaseMismatch.php | 7 + .../debug/Tests/Fixtures/reallyNotPsr0.php | 7 + .../debug/Tests/Fixtures2/RequiredTwice.php | 7 + .../vendor/symfony/debug/Tests/HeaderMock.php | 38 + .../debug/Tests/MockExceptionHandler.php | 24 + .../debug/Tests/phpt/debug_class_loader.phpt | 26 + .../Tests/phpt/decorate_exception_hander.phpt | 47 + .../debug/Tests/phpt/exception_rethrown.phpt | 35 + .../phpt/fatal_with_nested_handlers.phpt | 42 + lib/silex/vendor/symfony/debug/composer.json | 40 + .../vendor/symfony/debug/phpunit.xml.dist | 33 + .../symfony/event-dispatcher/.gitignore | 3 + .../symfony/event-dispatcher/CHANGELOG.md | 42 + .../ContainerAwareEventDispatcher.php | 197 ++ .../Debug/TraceableEventDispatcher.php | 322 +++ .../TraceableEventDispatcherInterface.php | 36 + .../Debug/WrappedListener.php | 114 + .../RegisterListenersPass.php | 139 + .../vendor/symfony/event-dispatcher/Event.php | 58 + .../event-dispatcher/EventDispatcher.php | 236 ++ .../EventDispatcherInterface.php | 93 + .../EventSubscriberInterface.php | 46 + .../symfony/event-dispatcher/GenericEvent.php | 175 ++ .../ImmutableEventDispatcher.php | 91 + .../vendor/symfony/event-dispatcher/LICENSE | 19 + .../vendor/symfony/event-dispatcher/README.md | 15 + .../Tests/AbstractEventDispatcherTest.php | 442 ++++ .../ContainerAwareEventDispatcherTest.php | 210 ++ .../Debug/TraceableEventDispatcherTest.php | 257 ++ .../RegisterListenersPassTest.php | 179 ++ .../Tests/EventDispatcherTest.php | 22 + .../event-dispatcher/Tests/EventTest.php | 55 + .../Tests/GenericEventTest.php | 140 + .../Tests/ImmutableEventDispatcherTest.php | 106 + .../symfony/event-dispatcher/composer.json | 47 + .../symfony/event-dispatcher/phpunit.xml.dist | 31 + .../vendor/symfony/http-foundation/.gitignore | 3 + .../symfony/http-foundation/AcceptHeader.php | 168 ++ .../http-foundation/AcceptHeaderItem.php | 209 ++ .../symfony/http-foundation/ApacheRequest.php | 43 + .../http-foundation/BinaryFileResponse.php | 359 +++ .../symfony/http-foundation/CHANGELOG.md | 159 ++ .../vendor/symfony/http-foundation/Cookie.php | 289 ++ .../Exception/ConflictingHeadersException.php | 21 + .../Exception/RequestExceptionInterface.php | 21 + .../SuspiciousOperationException.php | 20 + .../ExpressionRequestMatcher.php | 47 + .../File/Exception/AccessDeniedException.php | 28 + .../File/Exception/FileException.php | 21 + .../File/Exception/FileNotFoundException.php | 28 + .../Exception/UnexpectedTypeException.php | 20 + .../File/Exception/UploadException.php | 21 + .../symfony/http-foundation/File/File.php | 136 + .../File/MimeType/ExtensionGuesser.php | 94 + .../MimeType/ExtensionGuesserInterface.php | 27 + .../MimeType/FileBinaryMimeTypeGuesser.php | 85 + .../File/MimeType/FileinfoMimeTypeGuesser.php | 69 + .../MimeType/MimeTypeExtensionGuesser.php | 807 ++++++ .../File/MimeType/MimeTypeGuesser.php | 142 + .../MimeType/MimeTypeGuesserInterface.php | 35 + .../symfony/http-foundation/File/Stream.php | 28 + .../http-foundation/File/UploadedFile.php | 266 ++ .../symfony/http-foundation/FileBag.php | 144 + .../symfony/http-foundation/HeaderBag.php | 331 +++ .../symfony/http-foundation/IpUtils.php | 156 ++ .../symfony/http-foundation/JsonResponse.php | 220 ++ .../vendor/symfony/http-foundation/LICENSE | 19 + .../symfony/http-foundation/ParameterBag.php | 234 ++ .../vendor/symfony/http-foundation/README.md | 14 + .../http-foundation/RedirectResponse.php | 109 + .../symfony/http-foundation/Request.php | 2154 +++++++++++++++ .../http-foundation/RequestMatcher.php | 178 ++ .../RequestMatcherInterface.php | 27 + .../symfony/http-foundation/RequestStack.php | 103 + .../symfony/http-foundation/Response.php | 1298 +++++++++ .../http-foundation/ResponseHeaderBag.php | 340 +++ .../symfony/http-foundation/ServerBag.php | 102 + .../Session/Attribute/AttributeBag.php | 148 ++ .../Attribute/AttributeBagInterface.php | 72 + .../Attribute/NamespacedAttributeBag.php | 153 ++ .../Session/Flash/AutoExpireFlashBag.php | 161 ++ .../Session/Flash/FlashBag.php | 152 ++ .../Session/Flash/FlashBagInterface.php | 93 + .../http-foundation/Session/Session.php | 273 ++ .../Session/SessionBagInterface.php | 46 + .../Session/SessionBagProxy.php | 82 + .../Session/SessionInterface.php | 180 ++ .../Handler/AbstractSessionHandler.php | 168 ++ .../Handler/MemcacheSessionHandler.php | 120 + .../Handler/MemcachedSessionHandler.php | 122 + .../Storage/Handler/MongoDbSessionHandler.php | 255 ++ .../Handler/NativeFileSessionHandler.php | 55 + .../Storage/Handler/NativeSessionHandler.php | 24 + .../Storage/Handler/NullSessionHandler.php | 76 + .../Storage/Handler/PdoSessionHandler.php | 812 ++++++ .../Storage/Handler/StrictSessionHandler.php | 103 + .../Handler/WriteCheckSessionHandler.php | 92 + .../Session/Storage/MetadataBag.php | 168 ++ .../Storage/MockArraySessionStorage.php | 256 ++ .../Storage/MockFileSessionStorage.php | 152 ++ .../Session/Storage/NativeSessionStorage.php | 445 ++++ .../Storage/PhpBridgeSessionStorage.php | 59 + .../Session/Storage/Proxy/AbstractProxy.php | 122 + .../Session/Storage/Proxy/NativeProxy.php | 40 + .../Storage/Proxy/SessionHandlerProxy.php | 85 + .../Storage/SessionStorageInterface.php | 137 + .../http-foundation/StreamedResponse.php | 144 + .../Tests/AcceptHeaderItemTest.php | 113 + .../Tests/AcceptHeaderTest.php | 103 + .../Tests/ApacheRequestTest.php | 93 + .../Tests/BinaryFileResponseTest.php | 352 +++ .../http-foundation/Tests/CookieTest.php | 223 ++ .../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 0 -> 35 bytes .../Tests/File/Fixtures/test.gif | Bin 0 -> 35 bytes .../Tests/File/MimeType/MimeTypeTest.php | 90 + .../Tests/File/UploadedFileTest.php | 273 ++ .../http-foundation/Tests/FileBagTest.php | 175 ++ .../http-foundation/Tests/HeaderBagTest.php | 205 ++ .../http-foundation/Tests/IpUtilsTest.php | 104 + .../Tests/JsonResponseTest.php | 257 ++ .../Tests/ParameterBagTest.php | 194 ++ .../Tests/RedirectResponseTest.php | 97 + .../Tests/RequestMatcherTest.php | 151 ++ .../Tests/RequestStackTest.php | 70 + .../http-foundation/Tests/RequestTest.php | 2329 +++++++++++++++++ .../Tests/ResponseHeaderBagTest.php | 363 +++ .../http-foundation/Tests/ResponseTest.php | 1003 +++++++ .../Tests/ResponseTestCase.php | 89 + .../http-foundation/Tests/ServerBagTest.php | 170 ++ .../Session/Attribute/AttributeBagTest.php | 186 ++ .../Attribute/NamespacedAttributeBagTest.php | 182 ++ .../Session/Flash/AutoExpireFlashBagTest.php | 161 ++ .../Tests/Session/Flash/FlashBagTest.php | 132 + .../Tests/Session/SessionTest.php | 242 ++ .../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 | 376 +++ .../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 | 277 ++ .../Storage/PhpBridgeSessionStorageTest.php | 96 + .../Storage/Proxy/AbstractProxyTest.php | 113 + .../Session/Storage/Proxy/NativeProxyTest.php | 38 + .../Storage/Proxy/SessionHandlerProxyTest.php | 124 + .../Tests/StreamedResponseTest.php | 126 + .../Tests/schema/http-status-codes.rng | 31 + .../Tests/schema/iana-registry.rng | 198 ++ .../symfony/http-foundation/composer.json | 38 + .../symfony/http-foundation/phpunit.xml.dist | 31 + .../vendor/symfony/http-kernel/.gitignore | 5 + .../symfony/http-kernel/Bundle/Bundle.php | 229 ++ .../http-kernel/Bundle/BundleInterface.php | 84 + .../vendor/symfony/http-kernel/CHANGELOG.md | 151 ++ .../CacheClearer/CacheClearerInterface.php | 27 + .../CacheClearer/ChainCacheClearer.php | 56 + .../CacheClearer/Psr6CacheClearer.php | 58 + .../http-kernel/CacheWarmer/CacheWarmer.php | 32 + .../CacheWarmer/CacheWarmerAggregate.php | 90 + .../CacheWarmer/CacheWarmerInterface.php | 32 + .../CacheWarmer/WarmableInterface.php | 27 + .../vendor/symfony/http-kernel/Client.php | 205 ++ .../Config/EnvParametersResource.php | 99 + .../http-kernel/Config/FileLocator.php | 54 + .../Controller/ArgumentResolver.php | 94 + .../ArgumentResolver/DefaultValueResolver.php | 40 + .../RequestAttributeValueResolver.php | 40 + .../ArgumentResolver/RequestValueResolver.php | 40 + .../ArgumentResolver/ServiceValueResolver.php | 58 + .../ArgumentResolver/SessionValueResolver.php | 46 + .../VariadicValueResolver.php | 48 + .../Controller/ArgumentResolverInterface.php | 35 + .../ArgumentValueResolverInterface.php | 43 + .../ContainerControllerResolver.php | 121 + .../Controller/ControllerReference.php | 44 + .../Controller/ControllerResolver.php | 258 ++ .../ControllerResolverInterface.php | 57 + .../Controller/TraceableArgumentResolver.php | 44 + .../TraceableControllerResolver.php | 69 + .../ControllerMetadata/ArgumentMetadata.php | 115 + .../ArgumentMetadataFactory.php | 129 + .../ArgumentMetadataFactoryInterface.php | 27 + .../DataCollector/AjaxDataCollector.php | 38 + .../DataCollector/ConfigDataCollector.php | 332 +++ .../DataCollector/DataCollector.php | 128 + .../DataCollector/DataCollectorInterface.php | 37 + .../DataCollector/DumpDataCollector.php | 315 +++ .../DataCollector/EventDataCollector.php | 124 + .../DataCollector/ExceptionDataCollector.php | 112 + .../LateDataCollectorInterface.php | 25 + .../DataCollector/LoggerDataCollector.php | 278 ++ .../DataCollector/MemoryDataCollector.php | 120 + .../DataCollector/RequestDataCollector.php | 404 +++ .../DataCollector/RouterDataCollector.php | 108 + .../DataCollector/TimeDataCollector.php | 149 ++ .../DataCollector/Util/ValueExporter.php | 99 + .../http-kernel/Debug/FileLinkFormatter.php | 88 + .../Debug/TraceableEventDispatcher.php | 82 + .../AddAnnotatedClassesToCachePass.php | 153 ++ .../AddClassesToCachePass.php | 25 + .../ConfigurableExtension.php | 42 + .../ControllerArgumentValueResolverPass.php | 48 + .../DependencyInjection/Extension.php | 77 + .../FragmentRendererPass.php | 67 + .../LazyLoadingFragmentHandler.php | 79 + .../DependencyInjection/LoggerPass.php | 41 + .../MergeExtensionConfigurationPass.php | 41 + ...RegisterControllerArgumentLocatorsPass.php | 174 ++ ...oveEmptyControllerArgumentLocatorsPass.php | 76 + .../ResettableServicePass.php | 66 + .../DependencyInjection/ServicesResetter.php | 39 + .../Event/FilterControllerArgumentsEvent.php | 52 + .../Event/FilterControllerEvent.php | 53 + .../http-kernel/Event/FilterResponseEvent.php | 55 + .../http-kernel/Event/FinishRequestEvent.php | 21 + .../http-kernel/Event/GetResponseEvent.php | 58 + .../GetResponseForControllerResultEvent.php | 61 + .../Event/GetResponseForExceptionEvent.php | 90 + .../symfony/http-kernel/Event/KernelEvent.php | 82 + .../http-kernel/Event/PostResponseEvent.php | 46 + .../EventListener/AbstractSessionListener.php | 76 + .../AbstractTestSessionListener.php | 89 + .../AddRequestFormatsListener.php | 50 + .../EventListener/DebugHandlersListener.php | 156 ++ .../EventListener/DumpListener.php | 55 + .../EventListener/ExceptionListener.php | 129 + .../EventListener/FragmentListener.php | 99 + .../EventListener/LocaleListener.php | 83 + .../EventListener/ProfilerListener.php | 128 + .../EventListener/ResponseListener.php | 56 + .../EventListener/RouterListener.php | 171 ++ .../EventListener/SaveSessionListener.php | 66 + .../EventListener/SessionListener.php | 40 + .../StreamedResponseListener.php | 49 + .../EventListener/SurrogateListener.php | 65 + .../EventListener/TestSessionListener.php | 40 + .../EventListener/TranslatorListener.php | 69 + .../EventListener/ValidateRequestListener.php | 53 + .../Exception/AccessDeniedHttpException.php | 29 + .../Exception/BadRequestHttpException.php | 28 + .../Exception/ConflictHttpException.php | 28 + .../Exception/GoneHttpException.php | 28 + .../http-kernel/Exception/HttpException.php | 51 + .../Exception/HttpExceptionInterface.php | 34 + .../Exception/LengthRequiredHttpException.php | 28 + .../MethodNotAllowedHttpException.php | 31 + .../Exception/NotAcceptableHttpException.php | 28 + .../Exception/NotFoundHttpException.php | 28 + .../PreconditionFailedHttpException.php | 28 + .../PreconditionRequiredHttpException.php | 30 + .../ServiceUnavailableHttpException.php | 34 + .../TooManyRequestsHttpException.php | 36 + .../Exception/UnauthorizedHttpException.php | 31 + .../UnprocessableEntityHttpException.php | 28 + .../UnsupportedMediaTypeHttpException.php | 28 + .../AbstractSurrogateFragmentRenderer.php | 110 + .../Fragment/EsiFragmentRenderer.php | 28 + .../http-kernel/Fragment/FragmentHandler.php | 112 + .../Fragment/FragmentRendererInterface.php | 42 + .../Fragment/HIncludeFragmentRenderer.php | 165 ++ .../Fragment/InlineFragmentRenderer.php | 152 ++ .../Fragment/RoutableFragmentRenderer.php | 90 + .../Fragment/SsiFragmentRenderer.php | 28 + .../HttpCache/AbstractSurrogate.php | 134 + .../symfony/http-kernel/HttpCache/Esi.php | 115 + .../http-kernel/HttpCache/HttpCache.php | 714 +++++ .../HttpCache/ResponseCacheStrategy.php | 96 + .../ResponseCacheStrategyInterface.php | 37 + .../symfony/http-kernel/HttpCache/Ssi.php | 98 + .../symfony/http-kernel/HttpCache/Store.php | 485 ++++ .../http-kernel/HttpCache/StoreInterface.php | 83 + .../HttpCache/SurrogateInterface.php | 92 + .../vendor/symfony/http-kernel/HttpKernel.php | 299 +++ .../http-kernel/HttpKernelInterface.php | 43 + .../vendor/symfony/http-kernel/Kernel.php | 969 +++++++ .../symfony/http-kernel/KernelEvents.php | 103 + .../symfony/http-kernel/KernelInterface.php | 165 ++ lib/silex/vendor/symfony/http-kernel/LICENSE | 19 + .../http-kernel/Log/DebugLoggerInterface.php | 40 + .../vendor/symfony/http-kernel/Log/Logger.php | 111 + .../Profiler/FileProfilerStorage.php | 292 +++ .../symfony/http-kernel/Profiler/Profile.php | 287 ++ .../symfony/http-kernel/Profiler/Profiler.php | 265 ++ .../Profiler/ProfilerStorageInterface.php | 57 + .../vendor/symfony/http-kernel/README.md | 16 + .../http-kernel/RebootableInterface.php | 30 + .../http-kernel/Resources/welcome.html.php | 84 + .../http-kernel/TerminableInterface.php | 32 + .../http-kernel/Tests/Bundle/BundleTest.php | 104 + .../CacheClearer/ChainCacheClearerTest.php | 61 + .../CacheClearer/Psr6CacheClearerTest.php | 69 + .../CacheWarmer/CacheWarmerAggregateTest.php | 107 + .../Tests/CacheWarmer/CacheWarmerTest.php | 68 + .../symfony/http-kernel/Tests/ClientTest.php | 183 ++ .../Config/EnvParametersResourceTest.php | 110 + .../Tests/Config/FileLocatorTest.php | 48 + .../ServiceValueResolverTest.php | 93 + .../Tests/Controller/ArgumentResolverTest.php | 349 +++ .../ContainerControllerResolverTest.php | 308 +++ .../Controller/ControllerResolverTest.php | 331 +++ .../ArgumentMetadataFactoryTest.php | 148 ++ .../ArgumentMetadataTest.php | 46 + .../Tests/DataCollector/Compiler.log | 4 + .../DataCollector/ConfigDataCollectorTest.php | 66 + .../Tests/DataCollector/DataCollectorTest.php | 38 + .../DataCollector/DumpDataCollectorTest.php | 115 + .../ExceptionDataCollectorTest.php | 59 + .../DataCollector/LoggerDataCollectorTest.php | 144 + .../DataCollector/MemoryDataCollectorTest.php | 59 + .../RequestDataCollectorTest.php | 334 +++ .../DataCollector/TimeDataCollectorTest.php | 55 + .../DataCollector/Util/ValueExporterTest.php | 51 + .../Tests/Debug/FileLinkFormatterTest.php | 67 + .../Debug/TraceableEventDispatcherTest.php | 121 + .../AddAnnotatedClassesToCachePassTest.php | 99 + ...ontrollerArgumentValueResolverPassTest.php | 67 + .../FragmentRendererPassTest.php | 105 + .../LazyLoadingFragmentHandlerTest.php | 66 + .../DependencyInjection/LoggerPassTest.php | 56 + .../MergeExtensionConfigurationPassTest.php | 55 + ...sterControllerArgumentLocatorsPassTest.php | 389 +++ ...mptyControllerArgumentLocatorsPassTest.php | 148 ++ .../ResettableServicePassTest.php | 78 + .../ServicesResetterTest.php | 42 + .../FilterControllerArgumentsEventTest.php | 17 + .../GetResponseForExceptionEventTest.php | 27 + .../AddRequestFormatsListenerTest.php | 84 + .../DebugHandlersListenerTest.php | 155 ++ .../Tests/EventListener/DumpListenerTest.php | 81 + .../EventListener/ExceptionListenerTest.php | 178 ++ .../EventListener/FragmentListenerTest.php | 122 + .../EventListener/LocaleListenerTest.php | 102 + .../EventListener/ProfilerListenerTest.php | 71 + .../EventListener/ResponseListenerTest.php | 95 + .../EventListener/RouterListenerTest.php | 226 ++ .../EventListener/SaveSessionListenerTest.php | 49 + .../EventListener/SessionListenerTest.php | 79 + .../EventListener/SurrogateListenerTest.php | 67 + .../EventListener/TestSessionListenerTest.php | 164 ++ .../EventListener/TranslatorListenerTest.php | 118 + .../ValidateRequestListenerTest.php | 43 + .../AccessDeniedHttpExceptionTest.php | 13 + .../Exception/BadRequestHttpExceptionTest.php | 13 + .../Exception/ConflictHttpExceptionTest.php | 13 + .../Tests/Exception/GoneHttpExceptionTest.php | 13 + .../Tests/Exception/HttpExceptionTest.php | 53 + .../LengthRequiredHttpExceptionTest.php | 13 + .../MethodNotAllowedHttpExceptionTest.php | 24 + .../NotAcceptableHttpExceptionTest.php | 13 + .../Exception/NotFoundHttpExceptionTest.php | 13 + .../PreconditionFailedHttpExceptionTest.php | 13 + .../PreconditionRequiredHttpExceptionTest.php | 13 + .../ServiceUnavailableHttpExceptionTest.php | 29 + .../TooManyRequestsHttpExceptionTest.php | 29 + .../UnauthorizedHttpExceptionTest.php | 24 + .../UnprocessableEntityHttpExceptionTest.php | 28 + .../UnsupportedMediaTypeHttpExceptionTest.php | 23 + .../Tests/Fixtures/123/Kernel123.php | 37 + .../Fixtures/BaseBundle/Resources/foo.txt | 0 .../Fixtures/BaseBundle/Resources/hide.txt | 0 .../Fixtures/Bundle1Bundle/Resources/foo.txt | 0 .../Tests/Fixtures/Bundle1Bundle/bar.txt | 0 .../Tests/Fixtures/Bundle1Bundle/foo.txt | 0 .../Tests/Fixtures/Bundle2Bundle/foo.txt | 0 .../Fixtures/ChildBundle/Resources/foo.txt | 0 .../Fixtures/ChildBundle/Resources/hide.txt | 0 .../Tests/Fixtures/ClearableService.php | 13 + .../Controller/BasicTypesController.php | 19 + .../Fixtures/Controller/ExtendingRequest.php | 18 + .../Fixtures/Controller/ExtendingSession.php | 18 + .../Controller/NullableController.php | 19 + .../Controller/VariadicController.php | 19 + .../DataCollector/CloneVarDataCollector.php | 46 + .../ExtensionAbsentBundle.php | 18 + .../ExtensionLoadedExtension.php | 22 + .../ExtensionLoadedBundle.php | 18 + .../ExtensionNotValidExtension.php | 20 + .../ExtensionNotValidBundle.php | 18 + .../Command/BarCommand.php | 17 + .../Command/FooCommand.php | 22 + .../ExtensionPresentExtension.php | 22 + .../ExtensionPresentBundle.php | 18 + .../Tests/Fixtures/KernelForOverrideName.php | 28 + .../Tests/Fixtures/KernelForTest.php | 37 + .../Tests/Fixtures/KernelWithoutBundles.php | 33 + .../Tests/Fixtures/ResettableService.php | 13 + .../Fixtures/Resources/BaseBundle/hide.txt | 0 .../Fixtures/Resources/Bundle1Bundle/foo.txt | 0 .../Fixtures/Resources/ChildBundle/foo.txt | 0 .../Fixtures/Resources/FooBundle/foo.txt | 0 .../http-kernel/Tests/Fixtures/TestClient.php | 31 + .../Tests/Fixtures/TestEventDispatcher.php | 32 + .../Fragment/EsiFragmentRendererTest.php | 118 + .../Tests/Fragment/FragmentHandlerTest.php | 99 + .../Fragment/HIncludeFragmentRendererTest.php | 89 + .../Fragment/InlineFragmentRendererTest.php | 250 ++ .../Fragment/RoutableFragmentRendererTest.php | 94 + .../Fragment/SsiFragmentRendererTest.php | 97 + .../http-kernel/Tests/HttpCache/EsiTest.php | 248 ++ .../Tests/HttpCache/HttpCacheTest.php | 1482 +++++++++++ .../Tests/HttpCache/HttpCacheTestCase.php | 185 ++ .../HttpCache/ResponseCacheStrategyTest.php | 222 ++ .../http-kernel/Tests/HttpCache/SsiTest.php | 215 ++ .../http-kernel/Tests/HttpCache/StoreTest.php | 301 +++ .../Tests/HttpCache/TestHttpKernel.php | 92 + .../HttpCache/TestMultipleHttpKernel.php | 81 + .../http-kernel/Tests/HttpKernelTest.php | 403 +++ .../symfony/http-kernel/Tests/KernelTest.php | 1060 ++++++++ .../http-kernel/Tests/Log/LoggerTest.php | 212 ++ .../symfony/http-kernel/Tests/Logger.php | 88 + .../Profiler/FileProfilerStorageTest.php | 350 +++ .../Tests/Profiler/ProfilerTest.php | 105 + .../http-kernel/Tests/TestHttpKernel.php | 42 + .../http-kernel/Tests/UriSignerTest.php | 64 + .../vendor/symfony/http-kernel/UriSigner.php | 106 + .../vendor/symfony/http-kernel/composer.json | 72 + .../symfony/http-kernel/phpunit.xml.dist | 40 + .../vendor/symfony/polyfill-mbstring/LICENSE | 19 + .../symfony/polyfill-mbstring/Mbstring.php | 664 +++++ .../symfony/polyfill-mbstring/README.md | 13 + .../Resources/unidata/lowerCase.php | 1101 ++++++++ .../Resources/unidata/upperCase.php | 1109 ++++++++ .../symfony/polyfill-mbstring/bootstrap.php | 56 + .../symfony/polyfill-mbstring/composer.json | 34 + .../vendor/symfony/polyfill-php70/LICENSE | 19 + .../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 + .../symfony/polyfill-php70/bootstrap.php | 27 + .../symfony/polyfill-php70/composer.json | 33 + lib/silex/vendor/symfony/routing/.gitignore | 3 + .../symfony/routing/Annotation/Route.php | 144 + lib/silex/vendor/symfony/routing/CHANGELOG.md | 228 ++ .../vendor/symfony/routing/CompiledRoute.php | 169 ++ .../RoutingResolverPass.php | 49 + .../routing/Exception/ExceptionInterface.php | 21 + .../Exception/InvalidParameterException.php | 21 + .../Exception/MethodNotAllowedException.php | 41 + .../MissingMandatoryParametersException.php | 22 + .../Exception/NoConfigurationException.php | 21 + .../Exception/ResourceNotFoundException.php | 23 + .../Exception/RouteNotFoundException.php | 21 + .../ConfigurableRequirementsInterface.php | 55 + .../Generator/Dumper/GeneratorDumper.php | 37 + .../Dumper/GeneratorDumperInterface.php | 39 + .../Generator/Dumper/PhpGeneratorDumper.php | 118 + .../routing/Generator/UrlGenerator.php | 321 +++ .../Generator/UrlGeneratorInterface.php | 86 + lib/silex/vendor/symfony/routing/LICENSE | 19 + .../routing/Loader/AnnotationClassLoader.php | 269 ++ .../Loader/AnnotationDirectoryLoader.php | 93 + .../routing/Loader/AnnotationFileLoader.php | 142 + .../symfony/routing/Loader/ClosureLoader.php | 46 + .../Configurator/CollectionConfigurator.php | 79 + .../Configurator/ImportConfigurator.php | 49 + .../Loader/Configurator/RouteConfigurator.php | 31 + .../Configurator/RoutingConfigurator.php | 54 + .../Loader/Configurator/Traits/AddTrait.php | 54 + .../Loader/Configurator/Traits/RouteTrait.php | 131 + .../ServiceRouterLoader.php | 40 + .../routing/Loader/DirectoryLoader.php | 58 + .../routing/Loader/ObjectRouteLoader.php | 95 + .../symfony/routing/Loader/PhpFileLoader.php | 75 + .../symfony/routing/Loader/XmlFileLoader.php | 352 +++ .../symfony/routing/Loader/YamlFileLoader.php | 226 ++ .../Loader/schema/routing/routing-1.0.xsd | 148 ++ .../Matcher/Dumper/DumperCollection.php | 159 ++ .../routing/Matcher/Dumper/DumperRoute.php | 57 + .../routing/Matcher/Dumper/MatcherDumper.php | 37 + .../Matcher/Dumper/MatcherDumperInterface.php | 39 + .../Matcher/Dumper/PhpMatcherDumper.php | 441 ++++ .../Matcher/Dumper/StaticPrefixCollection.php | 238 ++ .../Matcher/RedirectableUrlMatcher.php | 65 + .../RedirectableUrlMatcherInterface.php | 31 + .../Matcher/RequestMatcherInterface.php | 39 + .../routing/Matcher/TraceableUrlMatcher.php | 141 + .../symfony/routing/Matcher/UrlMatcher.php | 250 ++ .../routing/Matcher/UrlMatcherInterface.php | 41 + lib/silex/vendor/symfony/routing/README.md | 13 + .../vendor/symfony/routing/RequestContext.php | 336 +++ .../routing/RequestContextAwareInterface.php | 27 + lib/silex/vendor/symfony/routing/Route.php | 558 ++++ .../symfony/routing/RouteCollection.php | 280 ++ .../routing/RouteCollectionBuilder.php | 380 +++ .../vendor/symfony/routing/RouteCompiler.php | 316 +++ .../routing/RouteCompilerInterface.php | 30 + lib/silex/vendor/symfony/routing/Router.php | 384 +++ .../symfony/routing/RouterInterface.php | 32 + .../routing/Tests/Annotation/RouteTest.php | 50 + .../routing/Tests/CompiledRouteTest.php | 27 + .../RoutingResolverPassTest.php | 36 + .../AnnotatedClasses/AbstractClass.php | 16 + .../Fixtures/AnnotatedClasses/BarClass.php | 19 + .../Fixtures/AnnotatedClasses/BazClass.php | 19 + .../Fixtures/AnnotatedClasses/FooClass.php | 16 + .../Fixtures/AnnotatedClasses/FooTrait.php | 13 + .../Tests/Fixtures/CustomCompiledRoute.php | 18 + .../Tests/Fixtures/CustomRouteCompiler.php | 26 + .../Tests/Fixtures/CustomXmlFileLoader.php | 26 + .../AnonymousClassInTrait.php | 24 + .../OtherAnnotatedClasses/NoStartTagClass.php | 3 + .../OtherAnnotatedClasses/VariadicClass.php | 19 + .../Tests/Fixtures/RedirectableUrlMatcher.php | 30 + .../routing/Tests/Fixtures/annotated.php | 0 .../routing/Tests/Fixtures/bad_format.yml | 3 + .../symfony/routing/Tests/Fixtures/bar.xml | 0 .../controller/import__controller.xml | 10 + .../controller/import__controller.yml | 4 + .../Fixtures/controller/import_controller.xml | 8 + .../Fixtures/controller/import_controller.yml | 3 + .../controller/import_override_defaults.xml | 10 + .../controller/import_override_defaults.yml | 5 + .../Fixtures/controller/override_defaults.xml | 10 + .../Fixtures/controller/override_defaults.yml | 5 + .../Tests/Fixtures/controller/routing.xml | 14 + .../Tests/Fixtures/controller/routing.yml | 11 + .../Fixtures/directory/recurse/routes1.yml | 2 + .../Fixtures/directory/recurse/routes2.yml | 2 + .../Tests/Fixtures/directory/routes3.yml | 2 + .../Fixtures/directory_import/import.yml | 3 + .../Tests/Fixtures/dumper/url_matcher0.php | 39 + .../Tests/Fixtures/dumper/url_matcher1.php | 312 +++ .../Tests/Fixtures/dumper/url_matcher2.php | 349 +++ .../Tests/Fixtures/dumper/url_matcher3.php | 53 + .../Tests/Fixtures/dumper/url_matcher4.php | 104 + .../Tests/Fixtures/dumper/url_matcher5.php | 162 ++ .../Tests/Fixtures/dumper/url_matcher6.php | 199 ++ .../Tests/Fixtures/dumper/url_matcher7.php | 229 ++ .../symfony/routing/Tests/Fixtures/empty.yml | 0 .../routing/Tests/Fixtures/file_resource.yml | 0 .../symfony/routing/Tests/Fixtures/foo.xml | 0 .../symfony/routing/Tests/Fixtures/foo1.xml | 0 .../routing/Tests/Fixtures/incomplete.yml | 2 + .../routing/Tests/Fixtures/list_defaults.xml | 20 + .../Tests/Fixtures/list_in_list_defaults.xml | 22 + .../Tests/Fixtures/list_in_map_defaults.xml | 22 + .../Tests/Fixtures/list_null_values.xml | 22 + .../routing/Tests/Fixtures/map_defaults.xml | 20 + .../Tests/Fixtures/map_in_list_defaults.xml | 22 + .../Tests/Fixtures/map_in_map_defaults.xml | 22 + .../Tests/Fixtures/map_null_values.xml | 22 + .../routing/Tests/Fixtures/missing_id.xml | 8 + .../routing/Tests/Fixtures/missing_path.xml | 8 + .../Tests/Fixtures/namespaceprefix.xml | 16 + .../Fixtures/nonesense_resource_plus_path.yml | 3 + .../nonesense_type_without_resource.yml | 3 + .../routing/Tests/Fixtures/nonvalid.xml | 10 + .../routing/Tests/Fixtures/nonvalid.yml | 1 + .../routing/Tests/Fixtures/nonvalid2.yml | 1 + .../routing/Tests/Fixtures/nonvalidkeys.yml | 3 + .../routing/Tests/Fixtures/nonvalidnode.xml | 8 + .../routing/Tests/Fixtures/nonvalidroute.xml | 12 + .../routing/Tests/Fixtures/null_values.xml | 12 + .../routing/Tests/Fixtures/php_dsl.php | 21 + .../routing/Tests/Fixtures/php_dsl_sub.php | 14 + .../Tests/Fixtures/scalar_defaults.xml | 33 + .../Tests/Fixtures/special_route_name.yml | 2 + .../routing/Tests/Fixtures/validpattern.php | 18 + .../routing/Tests/Fixtures/validpattern.xml | 15 + .../routing/Tests/Fixtures/validpattern.yml | 13 + .../routing/Tests/Fixtures/validresource.php | 18 + .../routing/Tests/Fixtures/validresource.xml | 13 + .../routing/Tests/Fixtures/validresource.yml | 8 + .../Fixtures/with_define_path_variable.php | 5 + .../routing/Tests/Fixtures/withdoctype.xml | 3 + .../Dumper/PhpGeneratorDumperTest.php | 181 ++ .../Tests/Generator/UrlGeneratorTest.php | 724 +++++ .../Loader/AbstractAnnotationLoaderTest.php | 33 + .../Loader/AnnotationClassLoaderTest.php | 255 ++ .../Loader/AnnotationDirectoryLoaderTest.php | 98 + .../Tests/Loader/AnnotationFileLoaderTest.php | 91 + .../Tests/Loader/ClosureLoaderTest.php | 49 + .../Tests/Loader/DirectoryLoaderTest.php | 74 + .../Tests/Loader/ObjectRouteLoaderTest.php | 123 + .../Tests/Loader/PhpFileLoaderTest.php | 120 + .../Tests/Loader/XmlFileLoaderTest.php | 364 +++ .../Tests/Loader/YamlFileLoaderTest.php | 185 ++ .../Matcher/Dumper/DumperCollectionTest.php | 34 + .../Matcher/Dumper/PhpMatcherDumperTest.php | 459 ++++ .../Dumper/StaticPrefixCollectionTest.php | 175 ++ .../Matcher/RedirectableUrlMatcherTest.php | 111 + .../Tests/Matcher/TraceableUrlMatcherTest.php | 122 + .../routing/Tests/Matcher/UrlMatcherTest.php | 441 ++++ .../routing/Tests/RequestContextTest.php | 160 ++ .../Tests/RouteCollectionBuilderTest.php | 364 +++ .../routing/Tests/RouteCollectionTest.php | 305 +++ .../routing/Tests/RouteCompilerTest.php | 389 +++ .../symfony/routing/Tests/RouteTest.php | 258 ++ .../symfony/routing/Tests/RouterTest.php | 162 ++ .../vendor/symfony/routing/composer.json | 56 + .../vendor/symfony/routing/phpunit.xml.dist | 30 + lib/silex/vendor/twig/twig/.editorconfig | 18 + lib/silex/vendor/twig/twig/.php_cs.dist | 16 + lib/silex/vendor/twig/twig/.travis.yml | 49 + lib/silex/vendor/twig/twig/CHANGELOG | 215 +- lib/silex/vendor/twig/twig/LICENSE | 2 +- lib/silex/vendor/twig/twig/composer.json | 12 +- lib/silex/vendor/twig/twig/doc/advanced.rst | 168 +- .../vendor/twig/twig/doc/advanced_legacy.rst | 14 +- lib/silex/vendor/twig/twig/doc/api.rst | 62 +- .../vendor/twig/twig/doc/coding_standards.rst | 6 +- lib/silex/vendor/twig/twig/doc/deprecated.rst | 90 +- .../vendor/twig/twig/doc/filters/date.rst | 6 + .../vendor/twig/twig/doc/filters/escape.rst | 5 +- .../vendor/twig/twig/doc/filters/length.rst | 12 +- .../twig/twig/doc/filters/number_format.rst | 11 + .../vendor/twig/twig/doc/filters/replace.rst | 2 +- .../twig/twig/doc/filters/striptags.rst | 14 + .../vendor/twig/twig/doc/filters/trim.rst | 18 +- .../vendor/twig/twig/doc/functions/block.rst | 26 + .../twig/twig/doc/functions/constant.rst | 11 + .../vendor/twig/twig/doc/functions/date.rst | 3 + .../twig/twig/doc/functions/include.rst | 10 +- .../vendor/twig/twig/doc/functions/range.rst | 17 +- lib/silex/vendor/twig/twig/doc/internals.rst | 8 +- lib/silex/vendor/twig/twig/doc/intro.rst | 9 +- lib/silex/vendor/twig/twig/doc/recipes.rst | 116 +- .../vendor/twig/twig/doc/tags/autoescape.rst | 2 - lib/silex/vendor/twig/twig/doc/tags/embed.rst | 8 +- .../vendor/twig/twig/doc/tags/extends.rst | 12 +- .../vendor/twig/twig/doc/tags/include.rst | 10 +- lib/silex/vendor/twig/twig/doc/tags/index.rst | 1 + lib/silex/vendor/twig/twig/doc/tags/macro.rst | 19 +- lib/silex/vendor/twig/twig/doc/tags/use.rst | 2 +- lib/silex/vendor/twig/twig/doc/tags/with.rst | 44 + lib/silex/vendor/twig/twig/doc/templates.rst | 73 +- .../vendor/twig/twig/doc/tests/empty.rst | 15 +- .../vendor/twig/twig/ext/twig/php_twig.h | 2 +- lib/silex/vendor/twig/twig/ext/twig/twig.c | 168 +- .../vendor/twig/twig/lib/Twig/Autoloader.php | 12 +- .../twig/twig/lib/Twig/BaseNodeVisitor.php | 16 +- .../twig/twig/lib/Twig/Cache/Filesystem.php | 93 + .../vendor/twig/twig/lib/Twig/Cache/Null.php | 40 + .../twig/twig/lib/Twig/CacheInterface.php | 58 + .../vendor/twig/twig/lib/Twig/Compiler.php | 65 +- .../twig/twig/lib/Twig/CompilerInterface.php | 6 +- .../twig/lib/Twig/ContainerRuntimeLoader.php | 39 + .../vendor/twig/twig/lib/Twig/Environment.php | 651 +++-- lib/silex/vendor/twig/twig/lib/Twig/Error.php | 167 +- .../twig/twig/lib/Twig/Error/Loader.php | 15 +- .../twig/twig/lib/Twig/Error/Runtime.php | 6 +- .../twig/twig/lib/Twig/Error/Syntax.php | 39 +- .../twig/lib/Twig/ExistsLoaderInterface.php | 4 +- .../twig/twig/lib/Twig/ExpressionParser.php | 182 +- .../vendor/twig/twig/lib/Twig/Extension.php | 54 +- .../twig/twig/lib/Twig/Extension/Core.php | 458 ++-- .../twig/twig/lib/Twig/Extension/Debug.php | 20 +- .../twig/twig/lib/Twig/Extension/Escaper.php | 46 +- .../lib/Twig/Extension/GlobalsInterface.php | 24 + .../Twig/Extension/InitRuntimeInterface.php | 24 + .../twig/lib/Twig/Extension/Optimizer.php | 14 +- .../twig/twig/lib/Twig/Extension/Profiler.php | 13 +- .../twig/twig/lib/Twig/Extension/Sandbox.php | 23 +- .../twig/twig/lib/Twig/Extension/Staging.php | 45 +- .../twig/lib/Twig/Extension/StringLoader.php | 20 +- .../twig/twig/lib/Twig/ExtensionInterface.php | 23 +- .../twig/lib/Twig/FactoryRuntimeLoader.php | 39 + .../Twig/FileExtensionEscapingStrategy.php | 18 +- .../vendor/twig/twig/lib/Twig/Filter.php | 2 +- .../twig/twig/lib/Twig/Filter/Function.php | 2 +- .../twig/twig/lib/Twig/Filter/Method.php | 4 +- .../vendor/twig/twig/lib/Twig/Filter/Node.php | 2 +- .../twig/lib/Twig/FilterCallableInterface.php | 2 +- .../twig/twig/lib/Twig/FilterInterface.php | 2 +- .../vendor/twig/twig/lib/Twig/Function.php | 2 +- .../twig/twig/lib/Twig/Function/Function.php | 4 +- .../twig/twig/lib/Twig/Function/Method.php | 6 +- .../twig/twig/lib/Twig/Function/Node.php | 2 +- .../lib/Twig/FunctionCallableInterface.php | 2 +- .../twig/twig/lib/Twig/FunctionInterface.php | 4 +- lib/silex/vendor/twig/twig/lib/Twig/Lexer.php | 51 +- .../twig/twig/lib/Twig/LexerInterface.php | 10 +- .../twig/twig/lib/Twig/Loader/Array.php | 38 +- .../twig/twig/lib/Twig/Loader/Chain.php | 65 +- .../twig/twig/lib/Twig/Loader/Filesystem.php | 84 +- .../twig/twig/lib/Twig/Loader/String.php | 27 +- .../twig/twig/lib/Twig/LoaderInterface.php | 6 +- .../vendor/twig/twig/lib/Twig/Markup.php | 4 +- lib/silex/vendor/twig/twig/lib/Twig/Node.php | 103 +- .../twig/twig/lib/Twig/Node/AutoEscape.php | 9 +- .../vendor/twig/twig/lib/Twig/Node/Block.php | 11 +- .../twig/lib/Twig/Node/BlockReference.php | 11 +- .../vendor/twig/twig/lib/Twig/Node/Body.php | 4 +- .../twig/twig/lib/Twig/Node/CheckSecurity.php | 10 +- .../vendor/twig/twig/lib/Twig/Node/Do.php | 9 +- .../vendor/twig/twig/lib/Twig/Node/Embed.php | 16 +- .../twig/twig/lib/Twig/Node/Expression.php | 6 +- .../twig/lib/Twig/Node/Expression/Array.php | 13 +- .../lib/Twig/Node/Expression/AssignName.php | 11 +- .../twig/lib/Twig/Node/Expression/Binary.php | 11 +- .../lib/Twig/Node/Expression/Binary/Add.php | 6 +- .../lib/Twig/Node/Expression/Binary/And.php | 6 +- .../Node/Expression/Binary/BitwiseAnd.php | 6 +- .../Twig/Node/Expression/Binary/BitwiseOr.php | 6 +- .../Node/Expression/Binary/BitwiseXor.php | 6 +- .../Twig/Node/Expression/Binary/Concat.php | 6 +- .../lib/Twig/Node/Expression/Binary/Div.php | 6 +- .../Twig/Node/Expression/Binary/EndsWith.php | 4 +- .../lib/Twig/Node/Expression/Binary/Equal.php | 4 +- .../Twig/Node/Expression/Binary/FloorDiv.php | 13 +- .../Twig/Node/Expression/Binary/Greater.php | 4 +- .../Node/Expression/Binary/GreaterEqual.php | 4 +- .../lib/Twig/Node/Expression/Binary/In.php | 9 +- .../lib/Twig/Node/Expression/Binary/Less.php | 4 +- .../Twig/Node/Expression/Binary/LessEqual.php | 4 +- .../Twig/Node/Expression/Binary/Matches.php | 4 +- .../lib/Twig/Node/Expression/Binary/Mod.php | 6 +- .../lib/Twig/Node/Expression/Binary/Mul.php | 6 +- .../Twig/Node/Expression/Binary/NotEqual.php | 4 +- .../lib/Twig/Node/Expression/Binary/NotIn.php | 9 +- .../lib/Twig/Node/Expression/Binary/Or.php | 6 +- .../lib/Twig/Node/Expression/Binary/Power.php | 13 +- .../lib/Twig/Node/Expression/Binary/Range.php | 9 +- .../Node/Expression/Binary/StartsWith.php | 4 +- .../lib/Twig/Node/Expression/Binary/Sub.php | 6 +- .../Twig/Node/Expression/BlockReference.php | 92 +- .../twig/lib/Twig/Node/Expression/Call.php | 156 +- .../lib/Twig/Node/Expression/Conditional.php | 6 +- .../lib/Twig/Node/Expression/Constant.php | 6 +- .../Node/Expression/ExtensionReference.php | 11 +- .../twig/lib/Twig/Node/Expression/Filter.php | 6 +- .../Twig/Node/Expression/Filter/Default.php | 12 +- .../lib/Twig/Node/Expression/Function.php | 13 +- .../twig/lib/Twig/Node/Expression/GetAttr.php | 21 +- .../lib/Twig/Node/Expression/MethodCall.php | 4 +- .../twig/lib/Twig/Node/Expression/Name.php | 32 +- .../lib/Twig/Node/Expression/NullCoalesce.php | 48 + .../twig/lib/Twig/Node/Expression/Parent.php | 11 +- .../lib/Twig/Node/Expression/TempName.php | 4 +- .../twig/lib/Twig/Node/Expression/Test.php | 11 +- .../Twig/Node/Expression/Test/Constant.php | 4 +- .../lib/Twig/Node/Expression/Test/Defined.php | 17 +- .../Twig/Node/Expression/Test/Divisibleby.php | 4 +- .../lib/Twig/Node/Expression/Test/Even.php | 4 +- .../lib/Twig/Node/Expression/Test/Null.php | 4 +- .../lib/Twig/Node/Expression/Test/Odd.php | 4 +- .../lib/Twig/Node/Expression/Test/Sameas.php | 4 +- .../twig/lib/Twig/Node/Expression/Unary.php | 6 +- .../lib/Twig/Node/Expression/Unary/Neg.php | 6 +- .../lib/Twig/Node/Expression/Unary/Not.php | 6 +- .../lib/Twig/Node/Expression/Unary/Pos.php | 6 +- .../vendor/twig/twig/lib/Twig/Node/Flush.php | 9 +- .../vendor/twig/twig/lib/Twig/Node/For.php | 27 +- .../twig/twig/lib/Twig/Node/ForLoop.php | 9 +- .../vendor/twig/twig/lib/Twig/Node/If.php | 20 +- .../vendor/twig/twig/lib/Twig/Node/Import.php | 13 +- .../twig/twig/lib/Twig/Node/Include.php | 24 +- .../vendor/twig/twig/lib/Twig/Node/Macro.php | 20 +- .../vendor/twig/twig/lib/Twig/Node/Module.php | 113 +- .../vendor/twig/twig/lib/Twig/Node/Print.php | 11 +- .../twig/twig/lib/Twig/Node/Sandbox.php | 11 +- .../twig/lib/Twig/Node/SandboxedPrint.php | 20 +- .../vendor/twig/twig/lib/Twig/Node/Set.php | 13 +- .../twig/twig/lib/Twig/Node/SetTemp.php | 7 +- .../twig/twig/lib/Twig/Node/Spaceless.php | 9 +- .../vendor/twig/twig/lib/Twig/Node/Text.php | 11 +- .../vendor/twig/twig/lib/Twig/Node/With.php | 64 + .../twig/lib/Twig/NodeCaptureInterface.php | 21 + .../twig/twig/lib/Twig/NodeInterface.php | 7 +- .../twig/lib/Twig/NodeOutputInterface.php | 4 +- .../twig/twig/lib/Twig/NodeTraverser.php | 19 +- .../twig/lib/Twig/NodeVisitor/Escaper.php | 23 +- .../twig/lib/Twig/NodeVisitor/Optimizer.php | 46 +- .../lib/Twig/NodeVisitor/SafeAnalysis.php | 14 +- .../twig/lib/Twig/NodeVisitor/Sandbox.php | 17 +- .../twig/lib/Twig/NodeVisitorInterface.php | 12 +- .../vendor/twig/twig/lib/Twig/Parser.php | 105 +- .../twig/twig/lib/Twig/ParserInterface.php | 6 +- .../twig/lib/Twig/Profiler/Dumper/Base.php | 62 + .../lib/Twig/Profiler/Dumper/Blackfire.php | 8 +- .../twig/lib/Twig/Profiler/Dumper/Html.php | 8 +- .../twig/lib/Twig/Profiler/Dumper/Text.php | 45 +- .../lib/Twig/Profiler/Node/EnterProfile.php | 7 +- .../lib/Twig/Profiler/Node/LeaveProfile.php | 7 +- .../Twig/Profiler/NodeVisitor/Profiler.php | 17 +- .../twig/twig/lib/Twig/Profiler/Profile.php | 22 +- .../twig/lib/Twig/RuntimeLoaderInterface.php | 29 + .../twig/lib/Twig/Sandbox/SecurityError.php | 4 +- .../Sandbox/SecurityNotAllowedFilterError.php | 4 +- .../SecurityNotAllowedFunctionError.php | 4 +- .../Sandbox/SecurityNotAllowedMethodError.php | 40 + .../SecurityNotAllowedPropertyError.php | 40 + .../Sandbox/SecurityNotAllowedTagError.php | 4 +- .../twig/lib/Twig/Sandbox/SecurityPolicy.php | 12 +- .../Twig/Sandbox/SecurityPolicyInterface.php | 4 +- .../twig/twig/lib/Twig/SimpleFilter.php | 11 +- .../twig/twig/lib/Twig/SimpleFunction.php | 11 +- .../vendor/twig/twig/lib/Twig/SimpleTest.php | 11 +- .../vendor/twig/twig/lib/Twig/Source.php | 53 + .../lib/Twig/SourceContextLoaderInterface.php | 33 + .../vendor/twig/twig/lib/Twig/Template.php | 311 ++- .../twig/twig/lib/Twig/TemplateInterface.php | 4 +- .../twig/twig/lib/Twig/TemplateWrapper.php | 133 + lib/silex/vendor/twig/twig/lib/Twig/Test.php | 2 +- .../twig/twig/lib/Twig/Test/Function.php | 2 +- .../lib/Twig/Test/IntegrationTestCase.php | 113 +- .../vendor/twig/twig/lib/Twig/Test/Method.php | 4 +- .../vendor/twig/twig/lib/Twig/Test/Node.php | 2 +- .../twig/twig/lib/Twig/Test/NodeTestCase.php | 25 +- .../twig/lib/Twig/TestCallableInterface.php | 2 +- .../twig/twig/lib/Twig/TestInterface.php | 2 +- lib/silex/vendor/twig/twig/lib/Twig/Token.php | 35 +- .../vendor/twig/twig/lib/Twig/TokenParser.php | 6 +- .../twig/lib/Twig/TokenParser/AutoEscape.php | 24 +- .../twig/twig/lib/Twig/TokenParser/Block.php | 24 +- .../twig/twig/lib/Twig/TokenParser/Do.php | 18 +- .../twig/twig/lib/Twig/TokenParser/Embed.php | 33 +- .../twig/lib/Twig/TokenParser/Extends.php | 28 +- .../twig/twig/lib/Twig/TokenParser/Filter.php | 20 +- .../twig/twig/lib/Twig/TokenParser/Flush.php | 18 +- .../twig/twig/lib/Twig/TokenParser/For.php | 30 +- .../twig/twig/lib/Twig/TokenParser/From.php | 20 +- .../twig/twig/lib/Twig/TokenParser/If.php | 22 +- .../twig/twig/lib/Twig/TokenParser/Import.php | 18 +- .../twig/lib/Twig/TokenParser/Include.php | 18 +- .../twig/twig/lib/Twig/TokenParser/Macro.php | 20 +- .../twig/lib/Twig/TokenParser/Sandbox.php | 25 +- .../twig/twig/lib/Twig/TokenParser/Set.php | 22 +- .../twig/lib/Twig/TokenParser/Spaceless.php | 18 +- .../twig/twig/lib/Twig/TokenParser/Use.php | 22 +- .../twig/twig/lib/Twig/TokenParser/With.php | 52 + .../twig/twig/lib/Twig/TokenParserBroker.php | 35 +- .../lib/Twig/TokenParserBrokerInterface.php | 8 +- .../twig/lib/Twig/TokenParserInterface.php | 12 +- .../vendor/twig/twig/lib/Twig/TokenStream.php | 91 +- .../lib/Twig/Util/DeprecationCollector.php | 14 +- .../lib/Twig/Util/TemplateDirIterator.php | 2 + lib/silex/vendor/twig/twig/phpunit.xml.dist | 8 + .../twig/twig/src/Cache/CacheInterface.php | 11 + .../twig/twig/src/Cache/FilesystemCache.php | 11 + .../vendor/twig/twig/src/Cache/NullCache.php | 11 + lib/silex/vendor/twig/twig/src/Compiler.php | 11 + .../vendor/twig/twig/src/Environment.php | 11 + .../vendor/twig/twig/src/Error/Error.php | 11 + .../twig/twig/src/Error/LoaderError.php | 11 + .../twig/twig/src/Error/RuntimeError.php | 11 + .../twig/twig/src/Error/SyntaxError.php | 11 + .../vendor/twig/twig/src/ExpressionParser.php | 11 + .../twig/src/Extension/AbstractExtension.php | 11 + .../twig/twig/src/Extension/CoreExtension.php | 11 + .../twig/src/Extension/DebugExtension.php | 11 + .../twig/src/Extension/EscaperExtension.php | 11 + .../twig/src/Extension/ExtensionInterface.php | 11 + .../twig/src/Extension/GlobalsInterface.php | 11 + .../src/Extension/InitRuntimeInterface.php | 11 + .../twig/src/Extension/OptimizerExtension.php | 11 + .../twig/src/Extension/ProfilerExtension.php | 11 + .../Extension/RuntimeExtensionInterface.php | 19 + .../twig/src/Extension/SandboxExtension.php | 11 + .../twig/src/Extension/StagingExtension.php | 11 + .../src/Extension/StringLoaderExtension.php | 11 + .../src/FileExtensionEscapingStrategy.php | 11 + lib/silex/vendor/twig/twig/src/Lexer.php | 11 + .../twig/twig/src/Loader/ArrayLoader.php | 11 + .../twig/twig/src/Loader/ChainLoader.php | 11 + .../twig/src/Loader/ExistsLoaderInterface.php | 11 + .../twig/twig/src/Loader/FilesystemLoader.php | 11 + .../twig/twig/src/Loader/LoaderInterface.php | 11 + .../Loader/SourceContextLoaderInterface.php | 11 + lib/silex/vendor/twig/twig/src/Markup.php | 11 + .../twig/twig/src/Node/AutoEscapeNode.php | 11 + .../vendor/twig/twig/src/Node/BlockNode.php | 11 + .../twig/twig/src/Node/BlockReferenceNode.php | 11 + .../vendor/twig/twig/src/Node/BodyNode.php | 11 + .../twig/twig/src/Node/CheckSecurityNode.php | 11 + .../vendor/twig/twig/src/Node/DoNode.php | 11 + .../vendor/twig/twig/src/Node/EmbedNode.php | 11 + .../Node/Expression/AbstractExpression.php | 11 + .../src/Node/Expression/ArrayExpression.php | 11 + .../Node/Expression/AssignNameExpression.php | 11 + .../Node/Expression/Binary/AbstractBinary.php | 11 + .../src/Node/Expression/Binary/AddBinary.php | 11 + .../src/Node/Expression/Binary/AndBinary.php | 11 + .../Expression/Binary/BitwiseAndBinary.php | 11 + .../Expression/Binary/BitwiseOrBinary.php | 11 + .../Expression/Binary/BitwiseXorBinary.php | 11 + .../Node/Expression/Binary/ConcatBinary.php | 11 + .../src/Node/Expression/Binary/DivBinary.php | 11 + .../Node/Expression/Binary/EndsWithBinary.php | 11 + .../Node/Expression/Binary/EqualBinary.php | 11 + .../Node/Expression/Binary/FloorDivBinary.php | 11 + .../Node/Expression/Binary/GreaterBinary.php | 11 + .../Expression/Binary/GreaterEqualBinary.php | 11 + .../src/Node/Expression/Binary/InBinary.php | 11 + .../src/Node/Expression/Binary/LessBinary.php | 11 + .../Expression/Binary/LessEqualBinary.php | 11 + .../Node/Expression/Binary/MatchesBinary.php | 11 + .../src/Node/Expression/Binary/ModBinary.php | 11 + .../src/Node/Expression/Binary/MulBinary.php | 11 + .../Node/Expression/Binary/NotEqualBinary.php | 11 + .../Node/Expression/Binary/NotInBinary.php | 11 + .../src/Node/Expression/Binary/OrBinary.php | 11 + .../Node/Expression/Binary/PowerBinary.php | 11 + .../Node/Expression/Binary/RangeBinary.php | 11 + .../Expression/Binary/StartsWithBinary.php | 11 + .../src/Node/Expression/Binary/SubBinary.php | 11 + .../Expression/BlockReferenceExpression.php | 11 + .../src/Node/Expression/CallExpression.php | 11 + .../Node/Expression/ConditionalExpression.php | 11 + .../Node/Expression/ConstantExpression.php | 11 + .../Node/Expression/Filter/DefaultFilter.php | 11 + .../src/Node/Expression/FilterExpression.php | 11 + .../Node/Expression/FunctionExpression.php | 11 + .../src/Node/Expression/GetAttrExpression.php | 11 + .../Node/Expression/MethodCallExpression.php | 11 + .../src/Node/Expression/NameExpression.php | 11 + .../Expression/NullCoalesceExpression.php | 11 + .../src/Node/Expression/ParentExpression.php | 11 + .../Node/Expression/TempNameExpression.php | 11 + .../src/Node/Expression/Test/ConstantTest.php | 11 + .../src/Node/Expression/Test/DefinedTest.php | 11 + .../Node/Expression/Test/DivisiblebyTest.php | 11 + .../src/Node/Expression/Test/EvenTest.php | 11 + .../src/Node/Expression/Test/NullTest.php | 11 + .../twig/src/Node/Expression/Test/OddTest.php | 11 + .../src/Node/Expression/Test/SameasTest.php | 11 + .../src/Node/Expression/TestExpression.php | 11 + .../Node/Expression/Unary/AbstractUnary.php | 11 + .../src/Node/Expression/Unary/NegUnary.php | 11 + .../src/Node/Expression/Unary/NotUnary.php | 11 + .../src/Node/Expression/Unary/PosUnary.php | 11 + .../vendor/twig/twig/src/Node/FlushNode.php | 11 + .../vendor/twig/twig/src/Node/ForLoopNode.php | 11 + .../vendor/twig/twig/src/Node/ForNode.php | 11 + .../vendor/twig/twig/src/Node/IfNode.php | 11 + .../vendor/twig/twig/src/Node/ImportNode.php | 11 + .../vendor/twig/twig/src/Node/IncludeNode.php | 11 + .../vendor/twig/twig/src/Node/MacroNode.php | 11 + .../vendor/twig/twig/src/Node/ModuleNode.php | 11 + lib/silex/vendor/twig/twig/src/Node/Node.php | 11 + .../twig/src/Node/NodeCaptureInterface.php | 11 + .../twig/src/Node/NodeOutputInterface.php | 11 + .../vendor/twig/twig/src/Node/PrintNode.php | 11 + .../vendor/twig/twig/src/Node/SandboxNode.php | 11 + .../twig/twig/src/Node/SandboxedPrintNode.php | 11 + .../vendor/twig/twig/src/Node/SetNode.php | 11 + .../vendor/twig/twig/src/Node/SetTempNode.php | 11 + .../twig/twig/src/Node/SpacelessNode.php | 11 + .../vendor/twig/twig/src/Node/TextNode.php | 11 + .../vendor/twig/twig/src/Node/WithNode.php | 11 + .../vendor/twig/twig/src/NodeTraverser.php | 11 + .../src/NodeVisitor/AbstractNodeVisitor.php | 11 + .../src/NodeVisitor/EscaperNodeVisitor.php | 11 + .../src/NodeVisitor/NodeVisitorInterface.php | 11 + .../src/NodeVisitor/OptimizerNodeVisitor.php | 11 + .../NodeVisitor/SafeAnalysisNodeVisitor.php | 11 + .../src/NodeVisitor/SandboxNodeVisitor.php | 11 + lib/silex/vendor/twig/twig/src/Parser.php | 11 + .../twig/src/Profiler/Dumper/BaseDumper.php | 11 + .../src/Profiler/Dumper/BlackfireDumper.php | 11 + .../twig/src/Profiler/Dumper/HtmlDumper.php | 11 + .../twig/src/Profiler/Dumper/TextDumper.php | 11 + .../src/Profiler/Node/EnterProfileNode.php | 11 + .../src/Profiler/Node/LeaveProfileNode.php | 11 + .../NodeVisitor/ProfilerNodeVisitor.php | 11 + .../vendor/twig/twig/src/Profiler/Profile.php | 11 + .../RuntimeLoader/ContainerRuntimeLoader.php | 11 + .../RuntimeLoader/FactoryRuntimeLoader.php | 11 + .../RuntimeLoader/RuntimeLoaderInterface.php | 11 + .../twig/twig/src/Sandbox/SecurityError.php | 11 + .../Sandbox/SecurityNotAllowedFilterError.php | 11 + .../SecurityNotAllowedFunctionError.php | 11 + .../Sandbox/SecurityNotAllowedMethodError.php | 11 + .../SecurityNotAllowedPropertyError.php | 11 + .../Sandbox/SecurityNotAllowedTagError.php | 11 + .../twig/twig/src/Sandbox/SecurityPolicy.php | 11 + .../src/Sandbox/SecurityPolicyInterface.php | 11 + lib/silex/vendor/twig/twig/src/Source.php | 11 + lib/silex/vendor/twig/twig/src/Template.php | 11 + .../vendor/twig/twig/src/TemplateWrapper.php | 11 + .../twig/src/Test/IntegrationTestCase.php | 11 + .../twig/twig/src/Test/NodeTestCase.php | 11 + lib/silex/vendor/twig/twig/src/Token.php | 11 + .../src/TokenParser/AbstractTokenParser.php | 11 + .../src/TokenParser/AutoEscapeTokenParser.php | 11 + .../twig/src/TokenParser/BlockTokenParser.php | 11 + .../twig/src/TokenParser/DoTokenParser.php | 11 + .../twig/src/TokenParser/EmbedTokenParser.php | 11 + .../src/TokenParser/ExtendsTokenParser.php | 11 + .../src/TokenParser/FilterTokenParser.php | 11 + .../twig/src/TokenParser/FlushTokenParser.php | 11 + .../twig/src/TokenParser/ForTokenParser.php | 11 + .../twig/src/TokenParser/FromTokenParser.php | 11 + .../twig/src/TokenParser/IfTokenParser.php | 11 + .../src/TokenParser/ImportTokenParser.php | 11 + .../src/TokenParser/IncludeTokenParser.php | 11 + .../twig/src/TokenParser/MacroTokenParser.php | 11 + .../src/TokenParser/SandboxTokenParser.php | 11 + .../twig/src/TokenParser/SetTokenParser.php | 11 + .../src/TokenParser/SpacelessTokenParser.php | 11 + .../src/TokenParser/TokenParserInterface.php | 11 + .../twig/src/TokenParser/UseTokenParser.php | 11 + .../twig/src/TokenParser/WithTokenParser.php | 11 + .../vendor/twig/twig/src/TokenStream.php | 11 + lib/silex/vendor/twig/twig/src/TwigFilter.php | 11 + .../vendor/twig/twig/src/TwigFunction.php | 11 + lib/silex/vendor/twig/twig/src/TwigTest.php | 11 + .../twig/src/Util/DeprecationCollector.php | 11 + .../twig/src/Util/TemplateDirIterator.php | 11 + .../twig/test/Twig/Tests/AutoloaderTest.php | 2 +- .../test/Twig/Tests/Cache/FilesystemTest.php | 193 ++ .../twig/test/Twig/Tests/CompilerTest.php | 4 +- .../Twig/Tests/ContainerRuntimeLoaderTest.php | 40 + .../test/Twig/Tests/CustomExtensionTest.php | 93 + .../twig/test/Twig/Tests/EnvironmentTest.php | 502 +++- .../twig/twig/test/Twig/Tests/ErrorTest.php | 89 +- .../test/Twig/Tests/ExpressionParserTest.php | 107 +- .../test/Twig/Tests/Extension/CoreTest.php | 251 +- .../test/Twig/Tests/Extension/SandboxTest.php | 90 +- .../Twig/Tests/FactoryRuntimeLoaderTest.php | 32 + .../twig/test/Twig/Tests/FileCachingTest.php | 52 +- .../FileExtensionEscapingStrategyTest.php | 2 +- .../twig/test/Twig/Tests/FilesystemHelper.php | 30 + .../Twig/Tests/Fixtures/autoescape/block.test | 21 + .../Twig/Tests/Fixtures/autoescape/name.test | 22 + .../child_contents_outside_blocks.test | 15 + ...ltiline_array_with_undefined_variable.test | 2 +- ...e_array_with_undefined_variable_again.test | 2 +- ...line_function_with_undefined_variable.test | 2 +- ...multiline_tag_with_undefined_variable.test | 2 +- .../syntax_error_in_reused_template.test | 2 +- .../Fixtures/exceptions/unclosed_tag.test | 2 +- .../Tests/Fixtures/expressions/_self.test | 8 + .../Fixtures/expressions/magic_call.test | 22 +- .../Tests/Fixtures/expressions/power.test | 20 + .../two_word_operators_as_variables.test | 2 +- .../Fixtures/filters/date_default_format.test | 2 +- .../filters/date_default_format_interval.test | 2 +- .../Fixtures/filters/escape_javascript.test | 8 + .../Twig/Tests/Fixtures/filters/length.test | 23 +- .../filters/number_format_default.test | 2 +- .../Fixtures/filters/replace_invalid_arg.test | 2 +- .../Tests/Fixtures/filters/static_calls.test | 10 + .../Twig/Tests/Fixtures/filters/trim.test | 12 + .../filters/urlencode_deprecated.test | 2 +- .../functions/block_with_template.test | 22 + .../functions/block_without_name.test | 12 + .../Twig/Tests/Fixtures/functions/date.test | 4 +- .../Tests/Fixtures/functions/magic_call.test | 8 + .../Fixtures/functions/magic_call53.test | 12 + .../recursive_block_with_inheritance.test | 2 +- .../Fixtures/functions/static_calls.test | 10 + .../Fixtures/macros/varargs_argument.test | 3 +- .../regression/combined_debug_info.test | 2 +- .../tags/block/block_unique_name.test | 2 +- .../tags/embed/complex_dynamic_parent.test | 35 + .../Fixtures/tags/embed/dynamic_parent.test | 35 + .../Tests/Fixtures/tags/embed/error_line.test | 2 +- .../Fixtures/tags/embed/with_extends.test | 5 +- .../Fixtures/tags/for/loop_not_defined.test | 2 +- .../tags/for/loop_not_defined_cond.test | 2 +- .../tags/inheritance/parent_in_a_block.test | 2 +- .../inheritance/parent_without_extends.test | 2 +- .../tags/macro/from_with_reserved_name.test | 2 +- .../tags/macro/import_with_reserved_nam.test | 2 +- .../Fixtures/tags/macro/reserved_name.test | 2 +- .../tags/raw/mixed_usage_with_raw.legacy.test | 2 +- .../Fixtures/tags/sandbox/not_valid1.test | 2 +- .../Fixtures/tags/sandbox/not_valid2.test | 2 +- .../Tests/Fixtures/tags/use/inheritance.test | 4 +- .../Tests/Fixtures/tags/use/inheritance2.test | 4 +- .../Fixtures/tags/use/use_with_parent.test | 24 + .../tags/verbatim/mixed_usage_with_raw.test | 2 +- .../Twig/Tests/Fixtures/tags/with/basic.test | 13 + .../Tests/Fixtures/tags/with/expression.test | 10 + .../Twig/Tests/Fixtures/tags/with/nested.test | 15 + .../Fixtures/tags/with/with_no_hash.test | 10 + .../Tests/Fixtures/tags/with/with_only.test | 10 + .../Twig/Tests/Fixtures/tests/defined.test | 21 + .../Fixtures/tests/defined_for_attribute.test | 35 + .../Fixtures/tests/defined_for_blocks.test | 38 + .../defined_for_blocks_with_template.test | 17 + .../Fixtures/tests/defined_for_constants.test | 14 + .../test/Twig/Tests/Fixtures/tests/empty.test | 53 +- .../Tests/Fixtures/tests/null_coalesce.test | 30 + .../twig/test/Twig/Tests/IntegrationTest.php | 84 +- .../autoescape/filename.legacy.test | 18 + .../functions/undefined_block.legacy.test | 12 + .../test/Twig/Tests/LegacyIntegrationTest.php | 9 + .../twig/twig/test/Twig/Tests/LexerTest.php | 141 +- .../twig/test/Twig/Tests/Loader/ArrayTest.php | 50 +- .../twig/test/Twig/Tests/Loader/ChainTest.php | 61 +- .../test/Twig/Tests/Loader/FilesystemTest.php | 79 +- .../Loader/Fixtures/phar/phar-sample.phar | Bin 0 -> 6786 bytes .../test/Twig/Tests/NativeExtensionTest.php | 9 +- .../Node/Expression/Binary/FloorDivTest.php | 2 +- .../Twig/Tests/Node/Expression/CallTest.php | 37 +- .../Twig/Tests/Node/Expression/FilterTest.php | 2 +- .../Tests/Node/Expression/FunctionTest.php | 2 +- .../Twig/Tests/Node/Expression/NameTest.php | 14 +- .../Node/Expression/NullCoalesceTest.php | 31 + .../Twig/Tests/Node/Expression/TestTest.php | 2 +- .../twig/test/Twig/Tests/Node/ForTest.php | 10 +- .../twig/twig/test/Twig/Tests/Node/IfTest.php | 2 +- .../twig/test/Twig/Tests/Node/IncludeTest.php | 2 +- .../twig/test/Twig/Tests/Node/MacroTest.php | 4 + .../twig/test/Twig/Tests/Node/ModuleTest.php | 68 +- .../twig/test/Twig/Tests/Node/SandboxTest.php | 2 +- .../Twig/Tests/Node/SandboxedPrintTest.php | 2 +- .../Twig/Tests/NodeVisitor/OptimizerTest.php | 20 +- .../twig/twig/test/Twig/Tests/ParserTest.php | 46 +- .../Tests/Profiler/Dumper/AbstractTest.php | 2 +- .../test/Twig/Tests/Profiler/ProfileTest.php | 12 +- .../twig/test/Twig/Tests/TemplateTest.php | 319 ++- .../test/Twig/Tests/TemplateWrapperTest.php | 64 + .../twig/test/Twig/Tests/TokenStreamTest.php | 24 +- .../Tests/Util/DeprecationCollectorTest.php | 42 + .../twig/test/Twig/Tests/escapingTest.php | 8 +- 1446 files changed, 100946 insertions(+), 5486 deletions(-) create mode 100644 lib/silex/vendor/composer/autoload_files.php create mode 100644 lib/silex/vendor/composer/autoload_static.php create mode 100644 lib/silex/vendor/paragonie/random_compat/LICENSE create mode 100644 lib/silex/vendor/paragonie/random_compat/build-phar.sh create mode 100644 lib/silex/vendor/paragonie/random_compat/composer.json create mode 100644 lib/silex/vendor/paragonie/random_compat/dist/random_compat.phar.pubkey create mode 100644 lib/silex/vendor/paragonie/random_compat/dist/random_compat.phar.pubkey.asc create mode 100644 lib/silex/vendor/paragonie/random_compat/lib/byte_safe_strings.php create mode 100644 lib/silex/vendor/paragonie/random_compat/lib/cast_to_int.php create mode 100644 lib/silex/vendor/paragonie/random_compat/lib/error_polyfill.php create mode 100644 lib/silex/vendor/paragonie/random_compat/lib/random.php create mode 100644 lib/silex/vendor/paragonie/random_compat/lib/random_bytes_com_dotnet.php create mode 100644 lib/silex/vendor/paragonie/random_compat/lib/random_bytes_dev_urandom.php create mode 100644 lib/silex/vendor/paragonie/random_compat/lib/random_bytes_libsodium.php create mode 100644 lib/silex/vendor/paragonie/random_compat/lib/random_bytes_libsodium_legacy.php create mode 100644 lib/silex/vendor/paragonie/random_compat/lib/random_bytes_mcrypt.php create mode 100644 lib/silex/vendor/paragonie/random_compat/lib/random_int.php create mode 100644 lib/silex/vendor/paragonie/random_compat/other/build_phar.php create mode 100644 lib/silex/vendor/paragonie/random_compat/psalm-autoload.php create mode 100644 lib/silex/vendor/paragonie/random_compat/psalm.xml create mode 100644 lib/silex/vendor/pimple/pimple/.travis.yml create mode 100644 lib/silex/vendor/pimple/pimple/CHANGELOG create mode 100644 lib/silex/vendor/pimple/pimple/ext/pimple/.gitignore create mode 100644 lib/silex/vendor/pimple/pimple/ext/pimple/README.md create mode 100644 lib/silex/vendor/pimple/pimple/ext/pimple/config.m4 create mode 100644 lib/silex/vendor/pimple/pimple/ext/pimple/config.w32 create mode 100644 lib/silex/vendor/pimple/pimple/ext/pimple/php_pimple.h create mode 100644 lib/silex/vendor/pimple/pimple/ext/pimple/pimple.c create mode 100644 lib/silex/vendor/pimple/pimple/ext/pimple/pimple_compat.h create mode 100644 lib/silex/vendor/pimple/pimple/ext/pimple/tests/001.phpt create mode 100644 lib/silex/vendor/pimple/pimple/ext/pimple/tests/002.phpt create mode 100644 lib/silex/vendor/pimple/pimple/ext/pimple/tests/003.phpt create mode 100644 lib/silex/vendor/pimple/pimple/ext/pimple/tests/004.phpt create mode 100644 lib/silex/vendor/pimple/pimple/ext/pimple/tests/005.phpt create mode 100644 lib/silex/vendor/pimple/pimple/ext/pimple/tests/006.phpt create mode 100644 lib/silex/vendor/pimple/pimple/ext/pimple/tests/007.phpt create mode 100644 lib/silex/vendor/pimple/pimple/ext/pimple/tests/008.phpt create mode 100644 lib/silex/vendor/pimple/pimple/ext/pimple/tests/009.phpt create mode 100644 lib/silex/vendor/pimple/pimple/ext/pimple/tests/010.phpt create mode 100644 lib/silex/vendor/pimple/pimple/ext/pimple/tests/011.phpt create mode 100644 lib/silex/vendor/pimple/pimple/ext/pimple/tests/012.phpt create mode 100644 lib/silex/vendor/pimple/pimple/ext/pimple/tests/013.phpt create mode 100644 lib/silex/vendor/pimple/pimple/ext/pimple/tests/014.phpt create mode 100644 lib/silex/vendor/pimple/pimple/ext/pimple/tests/015.phpt create mode 100644 lib/silex/vendor/pimple/pimple/ext/pimple/tests/016.phpt create mode 100644 lib/silex/vendor/pimple/pimple/ext/pimple/tests/017.phpt create mode 100644 lib/silex/vendor/pimple/pimple/ext/pimple/tests/017_1.phpt create mode 100644 lib/silex/vendor/pimple/pimple/ext/pimple/tests/018.phpt create mode 100644 lib/silex/vendor/pimple/pimple/ext/pimple/tests/019.phpt create mode 100644 lib/silex/vendor/pimple/pimple/ext/pimple/tests/bench.phpb create mode 100644 lib/silex/vendor/pimple/pimple/ext/pimple/tests/bench_shared.phpb create mode 100644 lib/silex/vendor/pimple/pimple/phpunit.xml.dist create mode 100644 lib/silex/vendor/pimple/pimple/src/Pimple/Container.php create mode 100644 lib/silex/vendor/pimple/pimple/src/Pimple/Exception/ExpectedInvokableException.php create mode 100644 lib/silex/vendor/pimple/pimple/src/Pimple/Exception/FrozenServiceException.php create mode 100644 lib/silex/vendor/pimple/pimple/src/Pimple/Exception/InvalidServiceIdentifierException.php create mode 100644 lib/silex/vendor/pimple/pimple/src/Pimple/Exception/UnknownIdentifierException.php create mode 100644 lib/silex/vendor/pimple/pimple/src/Pimple/Psr11/Container.php create mode 100644 lib/silex/vendor/pimple/pimple/src/Pimple/Psr11/ServiceLocator.php create mode 100644 lib/silex/vendor/pimple/pimple/src/Pimple/ServiceIterator.php create mode 100644 lib/silex/vendor/pimple/pimple/src/Pimple/ServiceProviderInterface.php create mode 100644 lib/silex/vendor/pimple/pimple/src/Pimple/Tests/Fixtures/Invokable.php create mode 100644 lib/silex/vendor/pimple/pimple/src/Pimple/Tests/Fixtures/NonInvokable.php create mode 100644 lib/silex/vendor/pimple/pimple/src/Pimple/Tests/Fixtures/PimpleServiceProvider.php create mode 100644 lib/silex/vendor/pimple/pimple/src/Pimple/Tests/Fixtures/Service.php create mode 100644 lib/silex/vendor/pimple/pimple/src/Pimple/Tests/PimpleServiceProviderInterfaceTest.php create mode 100644 lib/silex/vendor/pimple/pimple/src/Pimple/Tests/PimpleTest.php create mode 100644 lib/silex/vendor/pimple/pimple/src/Pimple/Tests/Psr11/ContainerTest.php create mode 100644 lib/silex/vendor/pimple/pimple/src/Pimple/Tests/Psr11/ServiceLocatorTest.php create mode 100644 lib/silex/vendor/pimple/pimple/src/Pimple/Tests/ServiceIteratorTest.php create mode 100644 lib/silex/vendor/psr/container/.gitignore create mode 100644 lib/silex/vendor/psr/container/LICENSE create mode 100644 lib/silex/vendor/psr/container/README.md create mode 100644 lib/silex/vendor/psr/container/composer.json create mode 100644 lib/silex/vendor/psr/container/src/ContainerExceptionInterface.php create mode 100644 lib/silex/vendor/psr/container/src/ContainerInterface.php create mode 100644 lib/silex/vendor/psr/container/src/NotFoundExceptionInterface.php create mode 100644 lib/silex/vendor/silex/silex/.travis.yml create mode 100644 lib/silex/vendor/silex/silex/doc/cookbook/guard_authentication.rst create mode 100644 lib/silex/vendor/silex/silex/doc/providers/asset.rst create mode 100644 lib/silex/vendor/silex/silex/doc/providers/csrf.rst create mode 100644 lib/silex/vendor/silex/silex/doc/providers/locale.rst create mode 100644 lib/silex/vendor/silex/silex/doc/providers/var_dumper.rst create mode 100644 lib/silex/vendor/silex/silex/src/Silex/Api/BootableProviderInterface.php create mode 100644 lib/silex/vendor/silex/silex/src/Silex/Api/ControllerProviderInterface.php create mode 100644 lib/silex/vendor/silex/silex/src/Silex/Api/EventListenerProviderInterface.php create mode 100644 lib/silex/vendor/silex/silex/src/Silex/Api/LICENSE create mode 100644 lib/silex/vendor/silex/silex/src/Silex/Api/composer.json create mode 100644 lib/silex/vendor/silex/silex/src/Silex/AppArgumentValueResolver.php create mode 100644 lib/silex/vendor/silex/silex/src/Silex/Provider/AssetServiceProvider.php create mode 100644 lib/silex/vendor/silex/silex/src/Silex/Provider/CsrfServiceProvider.php create mode 100644 lib/silex/vendor/silex/silex/src/Silex/Provider/ExceptionHandlerServiceProvider.php create mode 100644 lib/silex/vendor/silex/silex/src/Silex/Provider/Form/SilexFormExtension.php create mode 100644 lib/silex/vendor/silex/silex/src/Silex/Provider/HttpCache/HttpCache.php create mode 100644 lib/silex/vendor/silex/silex/src/Silex/Provider/HttpKernelServiceProvider.php create mode 100644 lib/silex/vendor/silex/silex/src/Silex/Provider/LICENSE create mode 100644 lib/silex/vendor/silex/silex/src/Silex/Provider/Locale/LocaleListener.php create mode 100644 lib/silex/vendor/silex/silex/src/Silex/Provider/LocaleServiceProvider.php create mode 100644 lib/silex/vendor/silex/silex/src/Silex/Provider/Routing/LazyRequestMatcher.php create mode 100644 lib/silex/vendor/silex/silex/src/Silex/Provider/Routing/RedirectableUrlMatcher.php create mode 100644 lib/silex/vendor/silex/silex/src/Silex/Provider/RoutingServiceProvider.php create mode 100644 lib/silex/vendor/silex/silex/src/Silex/Provider/Session/SessionListener.php create mode 100644 lib/silex/vendor/silex/silex/src/Silex/Provider/Session/TestSessionListener.php create mode 100644 lib/silex/vendor/silex/silex/src/Silex/Provider/Twig/RuntimeLoader.php create mode 100644 lib/silex/vendor/silex/silex/src/Silex/Provider/Validator/ConstraintValidatorFactory.php create mode 100644 lib/silex/vendor/silex/silex/src/Silex/Provider/VarDumperServiceProvider.php create mode 100644 lib/silex/vendor/silex/silex/src/Silex/Provider/composer.json create mode 100644 lib/silex/vendor/silex/silex/src/Silex/ViewListenerWrapper.php create mode 100644 lib/silex/vendor/silex/silex/tests/Silex/Tests/Fixtures/Php7Controller.php create mode 100644 lib/silex/vendor/silex/silex/tests/Silex/Tests/Fixtures/manifest.json create mode 100644 lib/silex/vendor/silex/silex/tests/Silex/Tests/LazyRequestMatcherTest.php create mode 100644 lib/silex/vendor/silex/silex/tests/Silex/Tests/Provider/AssetServiceProviderTest.php create mode 100644 lib/silex/vendor/silex/silex/tests/Silex/Tests/Provider/FormServiceProviderTest/DisableCsrfExtension.php create mode 100644 lib/silex/vendor/silex/silex/tests/Silex/Tests/Provider/RoutingServiceProviderTest.php create mode 100644 lib/silex/vendor/silex/silex/tests/Silex/Tests/Provider/SecurityServiceProviderTest/TokenAuthenticator.php create mode 100644 lib/silex/vendor/symfony/debug/.gitignore create mode 100644 lib/silex/vendor/symfony/debug/BufferingLogger.php create mode 100644 lib/silex/vendor/symfony/debug/CHANGELOG.md create mode 100644 lib/silex/vendor/symfony/debug/Debug.php create mode 100644 lib/silex/vendor/symfony/debug/DebugClassLoader.php create mode 100644 lib/silex/vendor/symfony/debug/ErrorHandler.php create mode 100644 lib/silex/vendor/symfony/debug/Exception/ClassNotFoundException.php create mode 100644 lib/silex/vendor/symfony/debug/Exception/ContextErrorException.php create mode 100644 lib/silex/vendor/symfony/debug/Exception/FatalErrorException.php create mode 100644 lib/silex/vendor/symfony/debug/Exception/FatalThrowableError.php create mode 100644 lib/silex/vendor/symfony/debug/Exception/FlattenException.php create mode 100644 lib/silex/vendor/symfony/debug/Exception/OutOfMemoryException.php create mode 100644 lib/silex/vendor/symfony/debug/Exception/SilencedErrorContext.php create mode 100644 lib/silex/vendor/symfony/debug/Exception/UndefinedFunctionException.php create mode 100644 lib/silex/vendor/symfony/debug/Exception/UndefinedMethodException.php create mode 100644 lib/silex/vendor/symfony/debug/ExceptionHandler.php create mode 100644 lib/silex/vendor/symfony/debug/FatalErrorHandler/ClassNotFoundFatalErrorHandler.php create mode 100644 lib/silex/vendor/symfony/debug/FatalErrorHandler/FatalErrorHandlerInterface.php create mode 100644 lib/silex/vendor/symfony/debug/FatalErrorHandler/UndefinedFunctionFatalErrorHandler.php create mode 100644 lib/silex/vendor/symfony/debug/FatalErrorHandler/UndefinedMethodFatalErrorHandler.php create mode 100644 lib/silex/vendor/symfony/debug/LICENSE create mode 100644 lib/silex/vendor/symfony/debug/README.md create mode 100644 lib/silex/vendor/symfony/debug/Resources/ext/README.md create mode 100644 lib/silex/vendor/symfony/debug/Resources/ext/config.m4 create mode 100644 lib/silex/vendor/symfony/debug/Resources/ext/config.w32 create mode 100644 lib/silex/vendor/symfony/debug/Resources/ext/php_symfony_debug.h create mode 100644 lib/silex/vendor/symfony/debug/Resources/ext/symfony_debug.c create mode 100644 lib/silex/vendor/symfony/debug/Resources/ext/tests/001.phpt create mode 100644 lib/silex/vendor/symfony/debug/Resources/ext/tests/002.phpt create mode 100644 lib/silex/vendor/symfony/debug/Resources/ext/tests/002_1.phpt create mode 100644 lib/silex/vendor/symfony/debug/Resources/ext/tests/003.phpt create mode 100644 lib/silex/vendor/symfony/debug/Tests/DebugClassLoaderTest.php create mode 100644 lib/silex/vendor/symfony/debug/Tests/ErrorHandlerTest.php create mode 100644 lib/silex/vendor/symfony/debug/Tests/Exception/FlattenExceptionTest.php create mode 100644 lib/silex/vendor/symfony/debug/Tests/ExceptionHandlerTest.php create mode 100644 lib/silex/vendor/symfony/debug/Tests/FatalErrorHandler/ClassNotFoundFatalErrorHandlerTest.php create mode 100644 lib/silex/vendor/symfony/debug/Tests/FatalErrorHandler/UndefinedFunctionFatalErrorHandlerTest.php create mode 100644 lib/silex/vendor/symfony/debug/Tests/FatalErrorHandler/UndefinedMethodFatalErrorHandlerTest.php create mode 100644 lib/silex/vendor/symfony/debug/Tests/Fixtures/AnnotatedClass.php create mode 100644 lib/silex/vendor/symfony/debug/Tests/Fixtures/ClassAlias.php create mode 100644 lib/silex/vendor/symfony/debug/Tests/Fixtures/DeprecatedClass.php create mode 100644 lib/silex/vendor/symfony/debug/Tests/Fixtures/DeprecatedInterface.php create mode 100644 lib/silex/vendor/symfony/debug/Tests/Fixtures/ExtendedFinalMethod.php create mode 100644 lib/silex/vendor/symfony/debug/Tests/Fixtures/FinalClass.php create mode 100644 lib/silex/vendor/symfony/debug/Tests/Fixtures/FinalMethod.php create mode 100644 lib/silex/vendor/symfony/debug/Tests/Fixtures/InternalClass.php create mode 100644 lib/silex/vendor/symfony/debug/Tests/Fixtures/InternalInterface.php create mode 100644 lib/silex/vendor/symfony/debug/Tests/Fixtures/InternalTrait.php create mode 100644 lib/silex/vendor/symfony/debug/Tests/Fixtures/InternalTrait2.php create mode 100644 lib/silex/vendor/symfony/debug/Tests/Fixtures/NonDeprecatedInterface.php create mode 100644 lib/silex/vendor/symfony/debug/Tests/Fixtures/PEARClass.php create mode 100644 lib/silex/vendor/symfony/debug/Tests/Fixtures/Throwing.php create mode 100644 lib/silex/vendor/symfony/debug/Tests/Fixtures/ToStringThrower.php create mode 100644 lib/silex/vendor/symfony/debug/Tests/Fixtures/casemismatch.php create mode 100644 lib/silex/vendor/symfony/debug/Tests/Fixtures/notPsr0Bis.php create mode 100644 lib/silex/vendor/symfony/debug/Tests/Fixtures/psr4/Psr4CaseMismatch.php create mode 100644 lib/silex/vendor/symfony/debug/Tests/Fixtures/reallyNotPsr0.php create mode 100644 lib/silex/vendor/symfony/debug/Tests/Fixtures2/RequiredTwice.php create mode 100644 lib/silex/vendor/symfony/debug/Tests/HeaderMock.php create mode 100644 lib/silex/vendor/symfony/debug/Tests/MockExceptionHandler.php create mode 100644 lib/silex/vendor/symfony/debug/Tests/phpt/debug_class_loader.phpt create mode 100644 lib/silex/vendor/symfony/debug/Tests/phpt/decorate_exception_hander.phpt create mode 100644 lib/silex/vendor/symfony/debug/Tests/phpt/exception_rethrown.phpt create mode 100644 lib/silex/vendor/symfony/debug/Tests/phpt/fatal_with_nested_handlers.phpt create mode 100644 lib/silex/vendor/symfony/debug/composer.json create mode 100644 lib/silex/vendor/symfony/debug/phpunit.xml.dist create mode 100644 lib/silex/vendor/symfony/event-dispatcher/.gitignore create mode 100644 lib/silex/vendor/symfony/event-dispatcher/CHANGELOG.md create mode 100644 lib/silex/vendor/symfony/event-dispatcher/ContainerAwareEventDispatcher.php create mode 100644 lib/silex/vendor/symfony/event-dispatcher/Debug/TraceableEventDispatcher.php create mode 100644 lib/silex/vendor/symfony/event-dispatcher/Debug/TraceableEventDispatcherInterface.php create mode 100644 lib/silex/vendor/symfony/event-dispatcher/Debug/WrappedListener.php create mode 100644 lib/silex/vendor/symfony/event-dispatcher/DependencyInjection/RegisterListenersPass.php create mode 100644 lib/silex/vendor/symfony/event-dispatcher/Event.php create mode 100644 lib/silex/vendor/symfony/event-dispatcher/EventDispatcher.php create mode 100644 lib/silex/vendor/symfony/event-dispatcher/EventDispatcherInterface.php create mode 100644 lib/silex/vendor/symfony/event-dispatcher/EventSubscriberInterface.php create mode 100644 lib/silex/vendor/symfony/event-dispatcher/GenericEvent.php create mode 100644 lib/silex/vendor/symfony/event-dispatcher/ImmutableEventDispatcher.php create mode 100644 lib/silex/vendor/symfony/event-dispatcher/LICENSE create mode 100644 lib/silex/vendor/symfony/event-dispatcher/README.md create mode 100644 lib/silex/vendor/symfony/event-dispatcher/Tests/AbstractEventDispatcherTest.php create mode 100644 lib/silex/vendor/symfony/event-dispatcher/Tests/ContainerAwareEventDispatcherTest.php create mode 100644 lib/silex/vendor/symfony/event-dispatcher/Tests/Debug/TraceableEventDispatcherTest.php create mode 100644 lib/silex/vendor/symfony/event-dispatcher/Tests/DependencyInjection/RegisterListenersPassTest.php create mode 100644 lib/silex/vendor/symfony/event-dispatcher/Tests/EventDispatcherTest.php create mode 100644 lib/silex/vendor/symfony/event-dispatcher/Tests/EventTest.php create mode 100644 lib/silex/vendor/symfony/event-dispatcher/Tests/GenericEventTest.php create mode 100644 lib/silex/vendor/symfony/event-dispatcher/Tests/ImmutableEventDispatcherTest.php create mode 100644 lib/silex/vendor/symfony/event-dispatcher/composer.json create mode 100644 lib/silex/vendor/symfony/event-dispatcher/phpunit.xml.dist create mode 100644 lib/silex/vendor/symfony/http-foundation/.gitignore create mode 100644 lib/silex/vendor/symfony/http-foundation/AcceptHeader.php create mode 100644 lib/silex/vendor/symfony/http-foundation/AcceptHeaderItem.php create mode 100644 lib/silex/vendor/symfony/http-foundation/ApacheRequest.php create mode 100644 lib/silex/vendor/symfony/http-foundation/BinaryFileResponse.php create mode 100644 lib/silex/vendor/symfony/http-foundation/CHANGELOG.md create mode 100644 lib/silex/vendor/symfony/http-foundation/Cookie.php create mode 100644 lib/silex/vendor/symfony/http-foundation/Exception/ConflictingHeadersException.php create mode 100644 lib/silex/vendor/symfony/http-foundation/Exception/RequestExceptionInterface.php create mode 100644 lib/silex/vendor/symfony/http-foundation/Exception/SuspiciousOperationException.php create mode 100644 lib/silex/vendor/symfony/http-foundation/ExpressionRequestMatcher.php create mode 100644 lib/silex/vendor/symfony/http-foundation/File/Exception/AccessDeniedException.php create mode 100644 lib/silex/vendor/symfony/http-foundation/File/Exception/FileException.php create mode 100644 lib/silex/vendor/symfony/http-foundation/File/Exception/FileNotFoundException.php create mode 100644 lib/silex/vendor/symfony/http-foundation/File/Exception/UnexpectedTypeException.php create mode 100644 lib/silex/vendor/symfony/http-foundation/File/Exception/UploadException.php create mode 100644 lib/silex/vendor/symfony/http-foundation/File/File.php create mode 100644 lib/silex/vendor/symfony/http-foundation/File/MimeType/ExtensionGuesser.php create mode 100644 lib/silex/vendor/symfony/http-foundation/File/MimeType/ExtensionGuesserInterface.php create mode 100644 lib/silex/vendor/symfony/http-foundation/File/MimeType/FileBinaryMimeTypeGuesser.php create mode 100644 lib/silex/vendor/symfony/http-foundation/File/MimeType/FileinfoMimeTypeGuesser.php create mode 100644 lib/silex/vendor/symfony/http-foundation/File/MimeType/MimeTypeExtensionGuesser.php create mode 100644 lib/silex/vendor/symfony/http-foundation/File/MimeType/MimeTypeGuesser.php create mode 100644 lib/silex/vendor/symfony/http-foundation/File/MimeType/MimeTypeGuesserInterface.php create mode 100644 lib/silex/vendor/symfony/http-foundation/File/Stream.php create mode 100644 lib/silex/vendor/symfony/http-foundation/File/UploadedFile.php create mode 100644 lib/silex/vendor/symfony/http-foundation/FileBag.php create mode 100644 lib/silex/vendor/symfony/http-foundation/HeaderBag.php create mode 100644 lib/silex/vendor/symfony/http-foundation/IpUtils.php create mode 100644 lib/silex/vendor/symfony/http-foundation/JsonResponse.php create mode 100644 lib/silex/vendor/symfony/http-foundation/LICENSE create mode 100644 lib/silex/vendor/symfony/http-foundation/ParameterBag.php create mode 100644 lib/silex/vendor/symfony/http-foundation/README.md create mode 100644 lib/silex/vendor/symfony/http-foundation/RedirectResponse.php create mode 100644 lib/silex/vendor/symfony/http-foundation/Request.php create mode 100644 lib/silex/vendor/symfony/http-foundation/RequestMatcher.php create mode 100644 lib/silex/vendor/symfony/http-foundation/RequestMatcherInterface.php create mode 100644 lib/silex/vendor/symfony/http-foundation/RequestStack.php create mode 100644 lib/silex/vendor/symfony/http-foundation/Response.php create mode 100644 lib/silex/vendor/symfony/http-foundation/ResponseHeaderBag.php create mode 100644 lib/silex/vendor/symfony/http-foundation/ServerBag.php create mode 100644 lib/silex/vendor/symfony/http-foundation/Session/Attribute/AttributeBag.php create mode 100644 lib/silex/vendor/symfony/http-foundation/Session/Attribute/AttributeBagInterface.php create mode 100644 lib/silex/vendor/symfony/http-foundation/Session/Attribute/NamespacedAttributeBag.php create mode 100644 lib/silex/vendor/symfony/http-foundation/Session/Flash/AutoExpireFlashBag.php create mode 100644 lib/silex/vendor/symfony/http-foundation/Session/Flash/FlashBag.php create mode 100644 lib/silex/vendor/symfony/http-foundation/Session/Flash/FlashBagInterface.php create mode 100644 lib/silex/vendor/symfony/http-foundation/Session/Session.php create mode 100644 lib/silex/vendor/symfony/http-foundation/Session/SessionBagInterface.php create mode 100644 lib/silex/vendor/symfony/http-foundation/Session/SessionBagProxy.php create mode 100644 lib/silex/vendor/symfony/http-foundation/Session/SessionInterface.php create mode 100644 lib/silex/vendor/symfony/http-foundation/Session/Storage/Handler/AbstractSessionHandler.php create mode 100644 lib/silex/vendor/symfony/http-foundation/Session/Storage/Handler/MemcacheSessionHandler.php create mode 100644 lib/silex/vendor/symfony/http-foundation/Session/Storage/Handler/MemcachedSessionHandler.php create mode 100644 lib/silex/vendor/symfony/http-foundation/Session/Storage/Handler/MongoDbSessionHandler.php create mode 100644 lib/silex/vendor/symfony/http-foundation/Session/Storage/Handler/NativeFileSessionHandler.php create mode 100644 lib/silex/vendor/symfony/http-foundation/Session/Storage/Handler/NativeSessionHandler.php create mode 100644 lib/silex/vendor/symfony/http-foundation/Session/Storage/Handler/NullSessionHandler.php create mode 100644 lib/silex/vendor/symfony/http-foundation/Session/Storage/Handler/PdoSessionHandler.php create mode 100644 lib/silex/vendor/symfony/http-foundation/Session/Storage/Handler/StrictSessionHandler.php create mode 100644 lib/silex/vendor/symfony/http-foundation/Session/Storage/Handler/WriteCheckSessionHandler.php create mode 100644 lib/silex/vendor/symfony/http-foundation/Session/Storage/MetadataBag.php create mode 100644 lib/silex/vendor/symfony/http-foundation/Session/Storage/MockArraySessionStorage.php create mode 100644 lib/silex/vendor/symfony/http-foundation/Session/Storage/MockFileSessionStorage.php create mode 100644 lib/silex/vendor/symfony/http-foundation/Session/Storage/NativeSessionStorage.php create mode 100644 lib/silex/vendor/symfony/http-foundation/Session/Storage/PhpBridgeSessionStorage.php create mode 100644 lib/silex/vendor/symfony/http-foundation/Session/Storage/Proxy/AbstractProxy.php create mode 100644 lib/silex/vendor/symfony/http-foundation/Session/Storage/Proxy/NativeProxy.php create mode 100644 lib/silex/vendor/symfony/http-foundation/Session/Storage/Proxy/SessionHandlerProxy.php create mode 100644 lib/silex/vendor/symfony/http-foundation/Session/Storage/SessionStorageInterface.php create mode 100644 lib/silex/vendor/symfony/http-foundation/StreamedResponse.php create mode 100644 lib/silex/vendor/symfony/http-foundation/Tests/AcceptHeaderItemTest.php create mode 100644 lib/silex/vendor/symfony/http-foundation/Tests/AcceptHeaderTest.php create mode 100644 lib/silex/vendor/symfony/http-foundation/Tests/ApacheRequestTest.php create mode 100644 lib/silex/vendor/symfony/http-foundation/Tests/BinaryFileResponseTest.php create mode 100644 lib/silex/vendor/symfony/http-foundation/Tests/CookieTest.php create mode 100644 lib/silex/vendor/symfony/http-foundation/Tests/ExpressionRequestMatcherTest.php create mode 100644 lib/silex/vendor/symfony/http-foundation/Tests/File/FakeFile.php create mode 100644 lib/silex/vendor/symfony/http-foundation/Tests/File/FileTest.php create mode 100644 lib/silex/vendor/symfony/http-foundation/Tests/File/Fixtures/.unknownextension create mode 100644 lib/silex/vendor/symfony/http-foundation/Tests/File/Fixtures/directory/.empty create mode 100644 lib/silex/vendor/symfony/http-foundation/Tests/File/Fixtures/other-file.example create mode 100644 lib/silex/vendor/symfony/http-foundation/Tests/File/Fixtures/test create mode 100644 lib/silex/vendor/symfony/http-foundation/Tests/File/Fixtures/test.gif create mode 100644 lib/silex/vendor/symfony/http-foundation/Tests/File/MimeType/MimeTypeTest.php create mode 100644 lib/silex/vendor/symfony/http-foundation/Tests/File/UploadedFileTest.php create mode 100644 lib/silex/vendor/symfony/http-foundation/Tests/FileBagTest.php create mode 100644 lib/silex/vendor/symfony/http-foundation/Tests/HeaderBagTest.php create mode 100644 lib/silex/vendor/symfony/http-foundation/Tests/IpUtilsTest.php create mode 100644 lib/silex/vendor/symfony/http-foundation/Tests/JsonResponseTest.php create mode 100644 lib/silex/vendor/symfony/http-foundation/Tests/ParameterBagTest.php create mode 100644 lib/silex/vendor/symfony/http-foundation/Tests/RedirectResponseTest.php create mode 100644 lib/silex/vendor/symfony/http-foundation/Tests/RequestMatcherTest.php create mode 100644 lib/silex/vendor/symfony/http-foundation/Tests/RequestStackTest.php create mode 100644 lib/silex/vendor/symfony/http-foundation/Tests/RequestTest.php create mode 100644 lib/silex/vendor/symfony/http-foundation/Tests/ResponseHeaderBagTest.php create mode 100644 lib/silex/vendor/symfony/http-foundation/Tests/ResponseTest.php create mode 100644 lib/silex/vendor/symfony/http-foundation/Tests/ResponseTestCase.php create mode 100644 lib/silex/vendor/symfony/http-foundation/Tests/ServerBagTest.php create mode 100644 lib/silex/vendor/symfony/http-foundation/Tests/Session/Attribute/AttributeBagTest.php create mode 100644 lib/silex/vendor/symfony/http-foundation/Tests/Session/Attribute/NamespacedAttributeBagTest.php create mode 100644 lib/silex/vendor/symfony/http-foundation/Tests/Session/Flash/AutoExpireFlashBagTest.php create mode 100644 lib/silex/vendor/symfony/http-foundation/Tests/Session/Flash/FlashBagTest.php create mode 100644 lib/silex/vendor/symfony/http-foundation/Tests/Session/SessionTest.php create mode 100644 lib/silex/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/AbstractSessionHandlerTest.php create mode 100644 lib/silex/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/common.inc create mode 100644 lib/silex/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/empty_destroys.expected create mode 100644 lib/silex/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/empty_destroys.php create mode 100644 lib/silex/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/read_only.expected create mode 100644 lib/silex/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/read_only.php create mode 100644 lib/silex/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/regenerate.expected create mode 100644 lib/silex/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/regenerate.php create mode 100644 lib/silex/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/storage.expected create mode 100644 lib/silex/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/storage.php create mode 100644 lib/silex/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/with_cookie.expected create mode 100644 lib/silex/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/with_cookie.php create mode 100644 lib/silex/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/with_cookie_and_session.expected create mode 100644 lib/silex/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/with_cookie_and_session.php create mode 100644 lib/silex/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/MemcacheSessionHandlerTest.php create mode 100644 lib/silex/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/MemcachedSessionHandlerTest.php create mode 100644 lib/silex/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/MongoDbSessionHandlerTest.php create mode 100644 lib/silex/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/NativeFileSessionHandlerTest.php create mode 100644 lib/silex/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/NativeSessionHandlerTest.php create mode 100644 lib/silex/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/NullSessionHandlerTest.php create mode 100644 lib/silex/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/PdoSessionHandlerTest.php create mode 100644 lib/silex/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/StrictSessionHandlerTest.php create mode 100644 lib/silex/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/WriteCheckSessionHandlerTest.php create mode 100644 lib/silex/vendor/symfony/http-foundation/Tests/Session/Storage/MetadataBagTest.php create mode 100644 lib/silex/vendor/symfony/http-foundation/Tests/Session/Storage/MockArraySessionStorageTest.php create mode 100644 lib/silex/vendor/symfony/http-foundation/Tests/Session/Storage/MockFileSessionStorageTest.php create mode 100644 lib/silex/vendor/symfony/http-foundation/Tests/Session/Storage/NativeSessionStorageTest.php create mode 100644 lib/silex/vendor/symfony/http-foundation/Tests/Session/Storage/PhpBridgeSessionStorageTest.php create mode 100644 lib/silex/vendor/symfony/http-foundation/Tests/Session/Storage/Proxy/AbstractProxyTest.php create mode 100644 lib/silex/vendor/symfony/http-foundation/Tests/Session/Storage/Proxy/NativeProxyTest.php create mode 100644 lib/silex/vendor/symfony/http-foundation/Tests/Session/Storage/Proxy/SessionHandlerProxyTest.php create mode 100644 lib/silex/vendor/symfony/http-foundation/Tests/StreamedResponseTest.php create mode 100644 lib/silex/vendor/symfony/http-foundation/Tests/schema/http-status-codes.rng create mode 100644 lib/silex/vendor/symfony/http-foundation/Tests/schema/iana-registry.rng create mode 100644 lib/silex/vendor/symfony/http-foundation/composer.json create mode 100644 lib/silex/vendor/symfony/http-foundation/phpunit.xml.dist create mode 100644 lib/silex/vendor/symfony/http-kernel/.gitignore create mode 100644 lib/silex/vendor/symfony/http-kernel/Bundle/Bundle.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Bundle/BundleInterface.php create mode 100644 lib/silex/vendor/symfony/http-kernel/CHANGELOG.md create mode 100644 lib/silex/vendor/symfony/http-kernel/CacheClearer/CacheClearerInterface.php create mode 100644 lib/silex/vendor/symfony/http-kernel/CacheClearer/ChainCacheClearer.php create mode 100644 lib/silex/vendor/symfony/http-kernel/CacheClearer/Psr6CacheClearer.php create mode 100644 lib/silex/vendor/symfony/http-kernel/CacheWarmer/CacheWarmer.php create mode 100644 lib/silex/vendor/symfony/http-kernel/CacheWarmer/CacheWarmerAggregate.php create mode 100644 lib/silex/vendor/symfony/http-kernel/CacheWarmer/CacheWarmerInterface.php create mode 100644 lib/silex/vendor/symfony/http-kernel/CacheWarmer/WarmableInterface.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Client.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Config/EnvParametersResource.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Config/FileLocator.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Controller/ArgumentResolver.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Controller/ArgumentResolver/DefaultValueResolver.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Controller/ArgumentResolver/RequestAttributeValueResolver.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Controller/ArgumentResolver/RequestValueResolver.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Controller/ArgumentResolver/ServiceValueResolver.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Controller/ArgumentResolver/SessionValueResolver.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Controller/ArgumentResolver/VariadicValueResolver.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Controller/ArgumentResolverInterface.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Controller/ArgumentValueResolverInterface.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Controller/ContainerControllerResolver.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Controller/ControllerReference.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Controller/ControllerResolver.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Controller/ControllerResolverInterface.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Controller/TraceableArgumentResolver.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Controller/TraceableControllerResolver.php create mode 100644 lib/silex/vendor/symfony/http-kernel/ControllerMetadata/ArgumentMetadata.php create mode 100644 lib/silex/vendor/symfony/http-kernel/ControllerMetadata/ArgumentMetadataFactory.php create mode 100644 lib/silex/vendor/symfony/http-kernel/ControllerMetadata/ArgumentMetadataFactoryInterface.php create mode 100644 lib/silex/vendor/symfony/http-kernel/DataCollector/AjaxDataCollector.php create mode 100644 lib/silex/vendor/symfony/http-kernel/DataCollector/ConfigDataCollector.php create mode 100644 lib/silex/vendor/symfony/http-kernel/DataCollector/DataCollector.php create mode 100644 lib/silex/vendor/symfony/http-kernel/DataCollector/DataCollectorInterface.php create mode 100644 lib/silex/vendor/symfony/http-kernel/DataCollector/DumpDataCollector.php create mode 100644 lib/silex/vendor/symfony/http-kernel/DataCollector/EventDataCollector.php create mode 100644 lib/silex/vendor/symfony/http-kernel/DataCollector/ExceptionDataCollector.php create mode 100644 lib/silex/vendor/symfony/http-kernel/DataCollector/LateDataCollectorInterface.php create mode 100644 lib/silex/vendor/symfony/http-kernel/DataCollector/LoggerDataCollector.php create mode 100644 lib/silex/vendor/symfony/http-kernel/DataCollector/MemoryDataCollector.php create mode 100644 lib/silex/vendor/symfony/http-kernel/DataCollector/RequestDataCollector.php create mode 100644 lib/silex/vendor/symfony/http-kernel/DataCollector/RouterDataCollector.php create mode 100644 lib/silex/vendor/symfony/http-kernel/DataCollector/TimeDataCollector.php create mode 100644 lib/silex/vendor/symfony/http-kernel/DataCollector/Util/ValueExporter.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Debug/FileLinkFormatter.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Debug/TraceableEventDispatcher.php create mode 100644 lib/silex/vendor/symfony/http-kernel/DependencyInjection/AddAnnotatedClassesToCachePass.php create mode 100644 lib/silex/vendor/symfony/http-kernel/DependencyInjection/AddClassesToCachePass.php create mode 100644 lib/silex/vendor/symfony/http-kernel/DependencyInjection/ConfigurableExtension.php create mode 100644 lib/silex/vendor/symfony/http-kernel/DependencyInjection/ControllerArgumentValueResolverPass.php create mode 100644 lib/silex/vendor/symfony/http-kernel/DependencyInjection/Extension.php create mode 100644 lib/silex/vendor/symfony/http-kernel/DependencyInjection/FragmentRendererPass.php create mode 100644 lib/silex/vendor/symfony/http-kernel/DependencyInjection/LazyLoadingFragmentHandler.php create mode 100644 lib/silex/vendor/symfony/http-kernel/DependencyInjection/LoggerPass.php create mode 100644 lib/silex/vendor/symfony/http-kernel/DependencyInjection/MergeExtensionConfigurationPass.php create mode 100644 lib/silex/vendor/symfony/http-kernel/DependencyInjection/RegisterControllerArgumentLocatorsPass.php create mode 100644 lib/silex/vendor/symfony/http-kernel/DependencyInjection/RemoveEmptyControllerArgumentLocatorsPass.php create mode 100644 lib/silex/vendor/symfony/http-kernel/DependencyInjection/ResettableServicePass.php create mode 100644 lib/silex/vendor/symfony/http-kernel/DependencyInjection/ServicesResetter.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Event/FilterControllerArgumentsEvent.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Event/FilterControllerEvent.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Event/FilterResponseEvent.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Event/FinishRequestEvent.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Event/GetResponseEvent.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Event/GetResponseForControllerResultEvent.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Event/GetResponseForExceptionEvent.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Event/KernelEvent.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Event/PostResponseEvent.php create mode 100644 lib/silex/vendor/symfony/http-kernel/EventListener/AbstractSessionListener.php create mode 100644 lib/silex/vendor/symfony/http-kernel/EventListener/AbstractTestSessionListener.php create mode 100644 lib/silex/vendor/symfony/http-kernel/EventListener/AddRequestFormatsListener.php create mode 100644 lib/silex/vendor/symfony/http-kernel/EventListener/DebugHandlersListener.php create mode 100644 lib/silex/vendor/symfony/http-kernel/EventListener/DumpListener.php create mode 100644 lib/silex/vendor/symfony/http-kernel/EventListener/ExceptionListener.php create mode 100644 lib/silex/vendor/symfony/http-kernel/EventListener/FragmentListener.php create mode 100644 lib/silex/vendor/symfony/http-kernel/EventListener/LocaleListener.php create mode 100644 lib/silex/vendor/symfony/http-kernel/EventListener/ProfilerListener.php create mode 100644 lib/silex/vendor/symfony/http-kernel/EventListener/ResponseListener.php create mode 100644 lib/silex/vendor/symfony/http-kernel/EventListener/RouterListener.php create mode 100644 lib/silex/vendor/symfony/http-kernel/EventListener/SaveSessionListener.php create mode 100644 lib/silex/vendor/symfony/http-kernel/EventListener/SessionListener.php create mode 100644 lib/silex/vendor/symfony/http-kernel/EventListener/StreamedResponseListener.php create mode 100644 lib/silex/vendor/symfony/http-kernel/EventListener/SurrogateListener.php create mode 100644 lib/silex/vendor/symfony/http-kernel/EventListener/TestSessionListener.php create mode 100644 lib/silex/vendor/symfony/http-kernel/EventListener/TranslatorListener.php create mode 100644 lib/silex/vendor/symfony/http-kernel/EventListener/ValidateRequestListener.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Exception/AccessDeniedHttpException.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Exception/BadRequestHttpException.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Exception/ConflictHttpException.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Exception/GoneHttpException.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Exception/HttpException.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Exception/HttpExceptionInterface.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Exception/LengthRequiredHttpException.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Exception/MethodNotAllowedHttpException.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Exception/NotAcceptableHttpException.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Exception/NotFoundHttpException.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Exception/PreconditionFailedHttpException.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Exception/PreconditionRequiredHttpException.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Exception/ServiceUnavailableHttpException.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Exception/TooManyRequestsHttpException.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Exception/UnauthorizedHttpException.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Exception/UnprocessableEntityHttpException.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Exception/UnsupportedMediaTypeHttpException.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Fragment/AbstractSurrogateFragmentRenderer.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Fragment/EsiFragmentRenderer.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Fragment/FragmentHandler.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Fragment/FragmentRendererInterface.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Fragment/HIncludeFragmentRenderer.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Fragment/InlineFragmentRenderer.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Fragment/RoutableFragmentRenderer.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Fragment/SsiFragmentRenderer.php create mode 100644 lib/silex/vendor/symfony/http-kernel/HttpCache/AbstractSurrogate.php create mode 100644 lib/silex/vendor/symfony/http-kernel/HttpCache/Esi.php create mode 100644 lib/silex/vendor/symfony/http-kernel/HttpCache/HttpCache.php create mode 100644 lib/silex/vendor/symfony/http-kernel/HttpCache/ResponseCacheStrategy.php create mode 100644 lib/silex/vendor/symfony/http-kernel/HttpCache/ResponseCacheStrategyInterface.php create mode 100644 lib/silex/vendor/symfony/http-kernel/HttpCache/Ssi.php create mode 100644 lib/silex/vendor/symfony/http-kernel/HttpCache/Store.php create mode 100644 lib/silex/vendor/symfony/http-kernel/HttpCache/StoreInterface.php create mode 100644 lib/silex/vendor/symfony/http-kernel/HttpCache/SurrogateInterface.php create mode 100644 lib/silex/vendor/symfony/http-kernel/HttpKernel.php create mode 100644 lib/silex/vendor/symfony/http-kernel/HttpKernelInterface.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Kernel.php create mode 100644 lib/silex/vendor/symfony/http-kernel/KernelEvents.php create mode 100644 lib/silex/vendor/symfony/http-kernel/KernelInterface.php create mode 100644 lib/silex/vendor/symfony/http-kernel/LICENSE create mode 100644 lib/silex/vendor/symfony/http-kernel/Log/DebugLoggerInterface.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Log/Logger.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Profiler/FileProfilerStorage.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Profiler/Profile.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Profiler/Profiler.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Profiler/ProfilerStorageInterface.php create mode 100644 lib/silex/vendor/symfony/http-kernel/README.md create mode 100644 lib/silex/vendor/symfony/http-kernel/RebootableInterface.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Resources/welcome.html.php create mode 100644 lib/silex/vendor/symfony/http-kernel/TerminableInterface.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Tests/Bundle/BundleTest.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Tests/CacheClearer/ChainCacheClearerTest.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Tests/CacheClearer/Psr6CacheClearerTest.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Tests/CacheWarmer/CacheWarmerAggregateTest.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Tests/CacheWarmer/CacheWarmerTest.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Tests/ClientTest.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Tests/Config/EnvParametersResourceTest.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Tests/Config/FileLocatorTest.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Tests/Controller/ArgumentResolver/ServiceValueResolverTest.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Tests/Controller/ArgumentResolverTest.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Tests/Controller/ContainerControllerResolverTest.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Tests/Controller/ControllerResolverTest.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Tests/ControllerMetadata/ArgumentMetadataFactoryTest.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Tests/ControllerMetadata/ArgumentMetadataTest.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Tests/DataCollector/Compiler.log create mode 100644 lib/silex/vendor/symfony/http-kernel/Tests/DataCollector/ConfigDataCollectorTest.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Tests/DataCollector/DataCollectorTest.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Tests/DataCollector/DumpDataCollectorTest.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Tests/DataCollector/ExceptionDataCollectorTest.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Tests/DataCollector/LoggerDataCollectorTest.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Tests/DataCollector/MemoryDataCollectorTest.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Tests/DataCollector/RequestDataCollectorTest.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Tests/DataCollector/TimeDataCollectorTest.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Tests/DataCollector/Util/ValueExporterTest.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Tests/Debug/FileLinkFormatterTest.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Tests/Debug/TraceableEventDispatcherTest.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Tests/DependencyInjection/AddAnnotatedClassesToCachePassTest.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Tests/DependencyInjection/ControllerArgumentValueResolverPassTest.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Tests/DependencyInjection/FragmentRendererPassTest.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Tests/DependencyInjection/LazyLoadingFragmentHandlerTest.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Tests/DependencyInjection/LoggerPassTest.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Tests/DependencyInjection/MergeExtensionConfigurationPassTest.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Tests/DependencyInjection/RegisterControllerArgumentLocatorsPassTest.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Tests/DependencyInjection/RemoveEmptyControllerArgumentLocatorsPassTest.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Tests/DependencyInjection/ResettableServicePassTest.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Tests/DependencyInjection/ServicesResetterTest.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Tests/Event/FilterControllerArgumentsEventTest.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Tests/Event/GetResponseForExceptionEventTest.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Tests/EventListener/AddRequestFormatsListenerTest.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Tests/EventListener/DebugHandlersListenerTest.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Tests/EventListener/DumpListenerTest.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Tests/EventListener/ExceptionListenerTest.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Tests/EventListener/FragmentListenerTest.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Tests/EventListener/LocaleListenerTest.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Tests/EventListener/ProfilerListenerTest.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Tests/EventListener/ResponseListenerTest.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Tests/EventListener/RouterListenerTest.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Tests/EventListener/SaveSessionListenerTest.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Tests/EventListener/SessionListenerTest.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Tests/EventListener/SurrogateListenerTest.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Tests/EventListener/TestSessionListenerTest.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Tests/EventListener/TranslatorListenerTest.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Tests/EventListener/ValidateRequestListenerTest.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Tests/Exception/AccessDeniedHttpExceptionTest.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Tests/Exception/BadRequestHttpExceptionTest.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Tests/Exception/ConflictHttpExceptionTest.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Tests/Exception/GoneHttpExceptionTest.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Tests/Exception/HttpExceptionTest.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Tests/Exception/LengthRequiredHttpExceptionTest.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Tests/Exception/MethodNotAllowedHttpExceptionTest.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Tests/Exception/NotAcceptableHttpExceptionTest.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Tests/Exception/NotFoundHttpExceptionTest.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Tests/Exception/PreconditionFailedHttpExceptionTest.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Tests/Exception/PreconditionRequiredHttpExceptionTest.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Tests/Exception/ServiceUnavailableHttpExceptionTest.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Tests/Exception/TooManyRequestsHttpExceptionTest.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Tests/Exception/UnauthorizedHttpExceptionTest.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Tests/Exception/UnprocessableEntityHttpExceptionTest.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Tests/Exception/UnsupportedMediaTypeHttpExceptionTest.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Tests/Fixtures/123/Kernel123.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Tests/Fixtures/BaseBundle/Resources/foo.txt create mode 100644 lib/silex/vendor/symfony/http-kernel/Tests/Fixtures/BaseBundle/Resources/hide.txt create mode 100644 lib/silex/vendor/symfony/http-kernel/Tests/Fixtures/Bundle1Bundle/Resources/foo.txt create mode 100644 lib/silex/vendor/symfony/http-kernel/Tests/Fixtures/Bundle1Bundle/bar.txt create mode 100644 lib/silex/vendor/symfony/http-kernel/Tests/Fixtures/Bundle1Bundle/foo.txt create mode 100644 lib/silex/vendor/symfony/http-kernel/Tests/Fixtures/Bundle2Bundle/foo.txt create mode 100644 lib/silex/vendor/symfony/http-kernel/Tests/Fixtures/ChildBundle/Resources/foo.txt create mode 100644 lib/silex/vendor/symfony/http-kernel/Tests/Fixtures/ChildBundle/Resources/hide.txt create mode 100644 lib/silex/vendor/symfony/http-kernel/Tests/Fixtures/ClearableService.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Tests/Fixtures/Controller/BasicTypesController.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Tests/Fixtures/Controller/ExtendingRequest.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Tests/Fixtures/Controller/ExtendingSession.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Tests/Fixtures/Controller/NullableController.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Tests/Fixtures/Controller/VariadicController.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Tests/Fixtures/DataCollector/CloneVarDataCollector.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Tests/Fixtures/ExtensionAbsentBundle/ExtensionAbsentBundle.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Tests/Fixtures/ExtensionLoadedBundle/DependencyInjection/ExtensionLoadedExtension.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Tests/Fixtures/ExtensionLoadedBundle/ExtensionLoadedBundle.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Tests/Fixtures/ExtensionNotValidBundle/DependencyInjection/ExtensionNotValidExtension.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Tests/Fixtures/ExtensionNotValidBundle/ExtensionNotValidBundle.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Tests/Fixtures/ExtensionPresentBundle/Command/BarCommand.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Tests/Fixtures/ExtensionPresentBundle/Command/FooCommand.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Tests/Fixtures/ExtensionPresentBundle/DependencyInjection/ExtensionPresentExtension.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Tests/Fixtures/ExtensionPresentBundle/ExtensionPresentBundle.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Tests/Fixtures/KernelForOverrideName.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Tests/Fixtures/KernelForTest.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Tests/Fixtures/KernelWithoutBundles.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Tests/Fixtures/ResettableService.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Tests/Fixtures/Resources/BaseBundle/hide.txt create mode 100644 lib/silex/vendor/symfony/http-kernel/Tests/Fixtures/Resources/Bundle1Bundle/foo.txt create mode 100644 lib/silex/vendor/symfony/http-kernel/Tests/Fixtures/Resources/ChildBundle/foo.txt create mode 100644 lib/silex/vendor/symfony/http-kernel/Tests/Fixtures/Resources/FooBundle/foo.txt create mode 100644 lib/silex/vendor/symfony/http-kernel/Tests/Fixtures/TestClient.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Tests/Fixtures/TestEventDispatcher.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Tests/Fragment/EsiFragmentRendererTest.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Tests/Fragment/FragmentHandlerTest.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Tests/Fragment/HIncludeFragmentRendererTest.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Tests/Fragment/InlineFragmentRendererTest.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Tests/Fragment/RoutableFragmentRendererTest.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Tests/Fragment/SsiFragmentRendererTest.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Tests/HttpCache/EsiTest.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Tests/HttpCache/HttpCacheTest.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Tests/HttpCache/HttpCacheTestCase.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Tests/HttpCache/ResponseCacheStrategyTest.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Tests/HttpCache/SsiTest.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Tests/HttpCache/StoreTest.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Tests/HttpCache/TestHttpKernel.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Tests/HttpCache/TestMultipleHttpKernel.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Tests/HttpKernelTest.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Tests/KernelTest.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Tests/Log/LoggerTest.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Tests/Logger.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Tests/Profiler/FileProfilerStorageTest.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Tests/Profiler/ProfilerTest.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Tests/TestHttpKernel.php create mode 100644 lib/silex/vendor/symfony/http-kernel/Tests/UriSignerTest.php create mode 100644 lib/silex/vendor/symfony/http-kernel/UriSigner.php create mode 100644 lib/silex/vendor/symfony/http-kernel/composer.json create mode 100644 lib/silex/vendor/symfony/http-kernel/phpunit.xml.dist create mode 100644 lib/silex/vendor/symfony/polyfill-mbstring/LICENSE create mode 100644 lib/silex/vendor/symfony/polyfill-mbstring/Mbstring.php create mode 100644 lib/silex/vendor/symfony/polyfill-mbstring/README.md create mode 100644 lib/silex/vendor/symfony/polyfill-mbstring/Resources/unidata/lowerCase.php create mode 100644 lib/silex/vendor/symfony/polyfill-mbstring/Resources/unidata/upperCase.php create mode 100644 lib/silex/vendor/symfony/polyfill-mbstring/bootstrap.php create mode 100644 lib/silex/vendor/symfony/polyfill-mbstring/composer.json create mode 100644 lib/silex/vendor/symfony/polyfill-php70/LICENSE create mode 100644 lib/silex/vendor/symfony/polyfill-php70/Php70.php create mode 100644 lib/silex/vendor/symfony/polyfill-php70/README.md create mode 100644 lib/silex/vendor/symfony/polyfill-php70/Resources/stubs/ArithmeticError.php create mode 100644 lib/silex/vendor/symfony/polyfill-php70/Resources/stubs/AssertionError.php create mode 100644 lib/silex/vendor/symfony/polyfill-php70/Resources/stubs/DivisionByZeroError.php create mode 100644 lib/silex/vendor/symfony/polyfill-php70/Resources/stubs/Error.php create mode 100644 lib/silex/vendor/symfony/polyfill-php70/Resources/stubs/ParseError.php create mode 100644 lib/silex/vendor/symfony/polyfill-php70/Resources/stubs/SessionUpdateTimestampHandlerInterface.php create mode 100644 lib/silex/vendor/symfony/polyfill-php70/Resources/stubs/TypeError.php create mode 100644 lib/silex/vendor/symfony/polyfill-php70/bootstrap.php create mode 100644 lib/silex/vendor/symfony/polyfill-php70/composer.json create mode 100644 lib/silex/vendor/symfony/routing/.gitignore create mode 100644 lib/silex/vendor/symfony/routing/Annotation/Route.php create mode 100644 lib/silex/vendor/symfony/routing/CHANGELOG.md create mode 100644 lib/silex/vendor/symfony/routing/CompiledRoute.php create mode 100644 lib/silex/vendor/symfony/routing/DependencyInjection/RoutingResolverPass.php create mode 100644 lib/silex/vendor/symfony/routing/Exception/ExceptionInterface.php create mode 100644 lib/silex/vendor/symfony/routing/Exception/InvalidParameterException.php create mode 100644 lib/silex/vendor/symfony/routing/Exception/MethodNotAllowedException.php create mode 100644 lib/silex/vendor/symfony/routing/Exception/MissingMandatoryParametersException.php create mode 100644 lib/silex/vendor/symfony/routing/Exception/NoConfigurationException.php create mode 100644 lib/silex/vendor/symfony/routing/Exception/ResourceNotFoundException.php create mode 100644 lib/silex/vendor/symfony/routing/Exception/RouteNotFoundException.php create mode 100644 lib/silex/vendor/symfony/routing/Generator/ConfigurableRequirementsInterface.php create mode 100644 lib/silex/vendor/symfony/routing/Generator/Dumper/GeneratorDumper.php create mode 100644 lib/silex/vendor/symfony/routing/Generator/Dumper/GeneratorDumperInterface.php create mode 100644 lib/silex/vendor/symfony/routing/Generator/Dumper/PhpGeneratorDumper.php create mode 100644 lib/silex/vendor/symfony/routing/Generator/UrlGenerator.php create mode 100644 lib/silex/vendor/symfony/routing/Generator/UrlGeneratorInterface.php create mode 100644 lib/silex/vendor/symfony/routing/LICENSE create mode 100644 lib/silex/vendor/symfony/routing/Loader/AnnotationClassLoader.php create mode 100644 lib/silex/vendor/symfony/routing/Loader/AnnotationDirectoryLoader.php create mode 100644 lib/silex/vendor/symfony/routing/Loader/AnnotationFileLoader.php create mode 100644 lib/silex/vendor/symfony/routing/Loader/ClosureLoader.php create mode 100644 lib/silex/vendor/symfony/routing/Loader/Configurator/CollectionConfigurator.php create mode 100644 lib/silex/vendor/symfony/routing/Loader/Configurator/ImportConfigurator.php create mode 100644 lib/silex/vendor/symfony/routing/Loader/Configurator/RouteConfigurator.php create mode 100644 lib/silex/vendor/symfony/routing/Loader/Configurator/RoutingConfigurator.php create mode 100644 lib/silex/vendor/symfony/routing/Loader/Configurator/Traits/AddTrait.php create mode 100644 lib/silex/vendor/symfony/routing/Loader/Configurator/Traits/RouteTrait.php create mode 100644 lib/silex/vendor/symfony/routing/Loader/DependencyInjection/ServiceRouterLoader.php create mode 100644 lib/silex/vendor/symfony/routing/Loader/DirectoryLoader.php create mode 100644 lib/silex/vendor/symfony/routing/Loader/ObjectRouteLoader.php create mode 100644 lib/silex/vendor/symfony/routing/Loader/PhpFileLoader.php create mode 100644 lib/silex/vendor/symfony/routing/Loader/XmlFileLoader.php create mode 100644 lib/silex/vendor/symfony/routing/Loader/YamlFileLoader.php create mode 100644 lib/silex/vendor/symfony/routing/Loader/schema/routing/routing-1.0.xsd create mode 100644 lib/silex/vendor/symfony/routing/Matcher/Dumper/DumperCollection.php create mode 100644 lib/silex/vendor/symfony/routing/Matcher/Dumper/DumperRoute.php create mode 100644 lib/silex/vendor/symfony/routing/Matcher/Dumper/MatcherDumper.php create mode 100644 lib/silex/vendor/symfony/routing/Matcher/Dumper/MatcherDumperInterface.php create mode 100644 lib/silex/vendor/symfony/routing/Matcher/Dumper/PhpMatcherDumper.php create mode 100644 lib/silex/vendor/symfony/routing/Matcher/Dumper/StaticPrefixCollection.php create mode 100644 lib/silex/vendor/symfony/routing/Matcher/RedirectableUrlMatcher.php create mode 100644 lib/silex/vendor/symfony/routing/Matcher/RedirectableUrlMatcherInterface.php create mode 100644 lib/silex/vendor/symfony/routing/Matcher/RequestMatcherInterface.php create mode 100644 lib/silex/vendor/symfony/routing/Matcher/TraceableUrlMatcher.php create mode 100644 lib/silex/vendor/symfony/routing/Matcher/UrlMatcher.php create mode 100644 lib/silex/vendor/symfony/routing/Matcher/UrlMatcherInterface.php create mode 100644 lib/silex/vendor/symfony/routing/README.md create mode 100644 lib/silex/vendor/symfony/routing/RequestContext.php create mode 100644 lib/silex/vendor/symfony/routing/RequestContextAwareInterface.php create mode 100644 lib/silex/vendor/symfony/routing/Route.php create mode 100644 lib/silex/vendor/symfony/routing/RouteCollection.php create mode 100644 lib/silex/vendor/symfony/routing/RouteCollectionBuilder.php create mode 100644 lib/silex/vendor/symfony/routing/RouteCompiler.php create mode 100644 lib/silex/vendor/symfony/routing/RouteCompilerInterface.php create mode 100644 lib/silex/vendor/symfony/routing/Router.php create mode 100644 lib/silex/vendor/symfony/routing/RouterInterface.php create mode 100644 lib/silex/vendor/symfony/routing/Tests/Annotation/RouteTest.php create mode 100644 lib/silex/vendor/symfony/routing/Tests/CompiledRouteTest.php create mode 100644 lib/silex/vendor/symfony/routing/Tests/DependencyInjection/RoutingResolverPassTest.php create mode 100644 lib/silex/vendor/symfony/routing/Tests/Fixtures/AnnotatedClasses/AbstractClass.php create mode 100644 lib/silex/vendor/symfony/routing/Tests/Fixtures/AnnotatedClasses/BarClass.php create mode 100644 lib/silex/vendor/symfony/routing/Tests/Fixtures/AnnotatedClasses/BazClass.php create mode 100644 lib/silex/vendor/symfony/routing/Tests/Fixtures/AnnotatedClasses/FooClass.php create mode 100644 lib/silex/vendor/symfony/routing/Tests/Fixtures/AnnotatedClasses/FooTrait.php create mode 100644 lib/silex/vendor/symfony/routing/Tests/Fixtures/CustomCompiledRoute.php create mode 100644 lib/silex/vendor/symfony/routing/Tests/Fixtures/CustomRouteCompiler.php create mode 100644 lib/silex/vendor/symfony/routing/Tests/Fixtures/CustomXmlFileLoader.php create mode 100644 lib/silex/vendor/symfony/routing/Tests/Fixtures/OtherAnnotatedClasses/AnonymousClassInTrait.php create mode 100644 lib/silex/vendor/symfony/routing/Tests/Fixtures/OtherAnnotatedClasses/NoStartTagClass.php create mode 100644 lib/silex/vendor/symfony/routing/Tests/Fixtures/OtherAnnotatedClasses/VariadicClass.php create mode 100644 lib/silex/vendor/symfony/routing/Tests/Fixtures/RedirectableUrlMatcher.php create mode 100644 lib/silex/vendor/symfony/routing/Tests/Fixtures/annotated.php create mode 100644 lib/silex/vendor/symfony/routing/Tests/Fixtures/bad_format.yml create mode 100644 lib/silex/vendor/symfony/routing/Tests/Fixtures/bar.xml create mode 100644 lib/silex/vendor/symfony/routing/Tests/Fixtures/controller/import__controller.xml create mode 100644 lib/silex/vendor/symfony/routing/Tests/Fixtures/controller/import__controller.yml create mode 100644 lib/silex/vendor/symfony/routing/Tests/Fixtures/controller/import_controller.xml create mode 100644 lib/silex/vendor/symfony/routing/Tests/Fixtures/controller/import_controller.yml create mode 100644 lib/silex/vendor/symfony/routing/Tests/Fixtures/controller/import_override_defaults.xml create mode 100644 lib/silex/vendor/symfony/routing/Tests/Fixtures/controller/import_override_defaults.yml create mode 100644 lib/silex/vendor/symfony/routing/Tests/Fixtures/controller/override_defaults.xml create mode 100644 lib/silex/vendor/symfony/routing/Tests/Fixtures/controller/override_defaults.yml create mode 100644 lib/silex/vendor/symfony/routing/Tests/Fixtures/controller/routing.xml create mode 100644 lib/silex/vendor/symfony/routing/Tests/Fixtures/controller/routing.yml create mode 100644 lib/silex/vendor/symfony/routing/Tests/Fixtures/directory/recurse/routes1.yml create mode 100644 lib/silex/vendor/symfony/routing/Tests/Fixtures/directory/recurse/routes2.yml create mode 100644 lib/silex/vendor/symfony/routing/Tests/Fixtures/directory/routes3.yml create mode 100644 lib/silex/vendor/symfony/routing/Tests/Fixtures/directory_import/import.yml create mode 100644 lib/silex/vendor/symfony/routing/Tests/Fixtures/dumper/url_matcher0.php create mode 100644 lib/silex/vendor/symfony/routing/Tests/Fixtures/dumper/url_matcher1.php create mode 100644 lib/silex/vendor/symfony/routing/Tests/Fixtures/dumper/url_matcher2.php create mode 100644 lib/silex/vendor/symfony/routing/Tests/Fixtures/dumper/url_matcher3.php create mode 100644 lib/silex/vendor/symfony/routing/Tests/Fixtures/dumper/url_matcher4.php create mode 100644 lib/silex/vendor/symfony/routing/Tests/Fixtures/dumper/url_matcher5.php create mode 100644 lib/silex/vendor/symfony/routing/Tests/Fixtures/dumper/url_matcher6.php create mode 100644 lib/silex/vendor/symfony/routing/Tests/Fixtures/dumper/url_matcher7.php create mode 100644 lib/silex/vendor/symfony/routing/Tests/Fixtures/empty.yml create mode 100644 lib/silex/vendor/symfony/routing/Tests/Fixtures/file_resource.yml create mode 100644 lib/silex/vendor/symfony/routing/Tests/Fixtures/foo.xml create mode 100644 lib/silex/vendor/symfony/routing/Tests/Fixtures/foo1.xml create mode 100644 lib/silex/vendor/symfony/routing/Tests/Fixtures/incomplete.yml create mode 100644 lib/silex/vendor/symfony/routing/Tests/Fixtures/list_defaults.xml create mode 100644 lib/silex/vendor/symfony/routing/Tests/Fixtures/list_in_list_defaults.xml create mode 100644 lib/silex/vendor/symfony/routing/Tests/Fixtures/list_in_map_defaults.xml create mode 100644 lib/silex/vendor/symfony/routing/Tests/Fixtures/list_null_values.xml create mode 100644 lib/silex/vendor/symfony/routing/Tests/Fixtures/map_defaults.xml create mode 100644 lib/silex/vendor/symfony/routing/Tests/Fixtures/map_in_list_defaults.xml create mode 100644 lib/silex/vendor/symfony/routing/Tests/Fixtures/map_in_map_defaults.xml create mode 100644 lib/silex/vendor/symfony/routing/Tests/Fixtures/map_null_values.xml create mode 100644 lib/silex/vendor/symfony/routing/Tests/Fixtures/missing_id.xml create mode 100644 lib/silex/vendor/symfony/routing/Tests/Fixtures/missing_path.xml create mode 100644 lib/silex/vendor/symfony/routing/Tests/Fixtures/namespaceprefix.xml create mode 100644 lib/silex/vendor/symfony/routing/Tests/Fixtures/nonesense_resource_plus_path.yml create mode 100644 lib/silex/vendor/symfony/routing/Tests/Fixtures/nonesense_type_without_resource.yml create mode 100644 lib/silex/vendor/symfony/routing/Tests/Fixtures/nonvalid.xml create mode 100644 lib/silex/vendor/symfony/routing/Tests/Fixtures/nonvalid.yml create mode 100644 lib/silex/vendor/symfony/routing/Tests/Fixtures/nonvalid2.yml create mode 100644 lib/silex/vendor/symfony/routing/Tests/Fixtures/nonvalidkeys.yml create mode 100644 lib/silex/vendor/symfony/routing/Tests/Fixtures/nonvalidnode.xml create mode 100644 lib/silex/vendor/symfony/routing/Tests/Fixtures/nonvalidroute.xml create mode 100644 lib/silex/vendor/symfony/routing/Tests/Fixtures/null_values.xml create mode 100644 lib/silex/vendor/symfony/routing/Tests/Fixtures/php_dsl.php create mode 100644 lib/silex/vendor/symfony/routing/Tests/Fixtures/php_dsl_sub.php create mode 100644 lib/silex/vendor/symfony/routing/Tests/Fixtures/scalar_defaults.xml create mode 100644 lib/silex/vendor/symfony/routing/Tests/Fixtures/special_route_name.yml create mode 100644 lib/silex/vendor/symfony/routing/Tests/Fixtures/validpattern.php create mode 100644 lib/silex/vendor/symfony/routing/Tests/Fixtures/validpattern.xml create mode 100644 lib/silex/vendor/symfony/routing/Tests/Fixtures/validpattern.yml create mode 100644 lib/silex/vendor/symfony/routing/Tests/Fixtures/validresource.php create mode 100644 lib/silex/vendor/symfony/routing/Tests/Fixtures/validresource.xml create mode 100644 lib/silex/vendor/symfony/routing/Tests/Fixtures/validresource.yml create mode 100644 lib/silex/vendor/symfony/routing/Tests/Fixtures/with_define_path_variable.php create mode 100644 lib/silex/vendor/symfony/routing/Tests/Fixtures/withdoctype.xml create mode 100644 lib/silex/vendor/symfony/routing/Tests/Generator/Dumper/PhpGeneratorDumperTest.php create mode 100644 lib/silex/vendor/symfony/routing/Tests/Generator/UrlGeneratorTest.php create mode 100644 lib/silex/vendor/symfony/routing/Tests/Loader/AbstractAnnotationLoaderTest.php create mode 100644 lib/silex/vendor/symfony/routing/Tests/Loader/AnnotationClassLoaderTest.php create mode 100644 lib/silex/vendor/symfony/routing/Tests/Loader/AnnotationDirectoryLoaderTest.php create mode 100644 lib/silex/vendor/symfony/routing/Tests/Loader/AnnotationFileLoaderTest.php create mode 100644 lib/silex/vendor/symfony/routing/Tests/Loader/ClosureLoaderTest.php create mode 100644 lib/silex/vendor/symfony/routing/Tests/Loader/DirectoryLoaderTest.php create mode 100644 lib/silex/vendor/symfony/routing/Tests/Loader/ObjectRouteLoaderTest.php create mode 100644 lib/silex/vendor/symfony/routing/Tests/Loader/PhpFileLoaderTest.php create mode 100644 lib/silex/vendor/symfony/routing/Tests/Loader/XmlFileLoaderTest.php create mode 100644 lib/silex/vendor/symfony/routing/Tests/Loader/YamlFileLoaderTest.php create mode 100644 lib/silex/vendor/symfony/routing/Tests/Matcher/Dumper/DumperCollectionTest.php create mode 100644 lib/silex/vendor/symfony/routing/Tests/Matcher/Dumper/PhpMatcherDumperTest.php create mode 100644 lib/silex/vendor/symfony/routing/Tests/Matcher/Dumper/StaticPrefixCollectionTest.php create mode 100644 lib/silex/vendor/symfony/routing/Tests/Matcher/RedirectableUrlMatcherTest.php create mode 100644 lib/silex/vendor/symfony/routing/Tests/Matcher/TraceableUrlMatcherTest.php create mode 100644 lib/silex/vendor/symfony/routing/Tests/Matcher/UrlMatcherTest.php create mode 100644 lib/silex/vendor/symfony/routing/Tests/RequestContextTest.php create mode 100644 lib/silex/vendor/symfony/routing/Tests/RouteCollectionBuilderTest.php create mode 100644 lib/silex/vendor/symfony/routing/Tests/RouteCollectionTest.php create mode 100644 lib/silex/vendor/symfony/routing/Tests/RouteCompilerTest.php create mode 100644 lib/silex/vendor/symfony/routing/Tests/RouteTest.php create mode 100644 lib/silex/vendor/symfony/routing/Tests/RouterTest.php create mode 100644 lib/silex/vendor/symfony/routing/composer.json create mode 100644 lib/silex/vendor/symfony/routing/phpunit.xml.dist create mode 100644 lib/silex/vendor/twig/twig/.editorconfig create mode 100644 lib/silex/vendor/twig/twig/.php_cs.dist create mode 100644 lib/silex/vendor/twig/twig/.travis.yml create mode 100644 lib/silex/vendor/twig/twig/doc/tags/with.rst create mode 100644 lib/silex/vendor/twig/twig/lib/Twig/Cache/Filesystem.php create mode 100644 lib/silex/vendor/twig/twig/lib/Twig/Cache/Null.php create mode 100644 lib/silex/vendor/twig/twig/lib/Twig/CacheInterface.php create mode 100644 lib/silex/vendor/twig/twig/lib/Twig/ContainerRuntimeLoader.php create mode 100644 lib/silex/vendor/twig/twig/lib/Twig/Extension/GlobalsInterface.php create mode 100644 lib/silex/vendor/twig/twig/lib/Twig/Extension/InitRuntimeInterface.php create mode 100644 lib/silex/vendor/twig/twig/lib/Twig/FactoryRuntimeLoader.php create mode 100644 lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/NullCoalesce.php create mode 100644 lib/silex/vendor/twig/twig/lib/Twig/Node/With.php create mode 100644 lib/silex/vendor/twig/twig/lib/Twig/NodeCaptureInterface.php create mode 100644 lib/silex/vendor/twig/twig/lib/Twig/Profiler/Dumper/Base.php create mode 100644 lib/silex/vendor/twig/twig/lib/Twig/RuntimeLoaderInterface.php create mode 100644 lib/silex/vendor/twig/twig/lib/Twig/Sandbox/SecurityNotAllowedMethodError.php create mode 100644 lib/silex/vendor/twig/twig/lib/Twig/Sandbox/SecurityNotAllowedPropertyError.php create mode 100644 lib/silex/vendor/twig/twig/lib/Twig/Source.php create mode 100644 lib/silex/vendor/twig/twig/lib/Twig/SourceContextLoaderInterface.php create mode 100644 lib/silex/vendor/twig/twig/lib/Twig/TemplateWrapper.php create mode 100644 lib/silex/vendor/twig/twig/lib/Twig/TokenParser/With.php create mode 100644 lib/silex/vendor/twig/twig/src/Cache/CacheInterface.php create mode 100644 lib/silex/vendor/twig/twig/src/Cache/FilesystemCache.php create mode 100644 lib/silex/vendor/twig/twig/src/Cache/NullCache.php create mode 100644 lib/silex/vendor/twig/twig/src/Compiler.php create mode 100644 lib/silex/vendor/twig/twig/src/Environment.php create mode 100644 lib/silex/vendor/twig/twig/src/Error/Error.php create mode 100644 lib/silex/vendor/twig/twig/src/Error/LoaderError.php create mode 100644 lib/silex/vendor/twig/twig/src/Error/RuntimeError.php create mode 100644 lib/silex/vendor/twig/twig/src/Error/SyntaxError.php create mode 100644 lib/silex/vendor/twig/twig/src/ExpressionParser.php create mode 100644 lib/silex/vendor/twig/twig/src/Extension/AbstractExtension.php create mode 100644 lib/silex/vendor/twig/twig/src/Extension/CoreExtension.php create mode 100644 lib/silex/vendor/twig/twig/src/Extension/DebugExtension.php create mode 100644 lib/silex/vendor/twig/twig/src/Extension/EscaperExtension.php create mode 100644 lib/silex/vendor/twig/twig/src/Extension/ExtensionInterface.php create mode 100644 lib/silex/vendor/twig/twig/src/Extension/GlobalsInterface.php create mode 100644 lib/silex/vendor/twig/twig/src/Extension/InitRuntimeInterface.php create mode 100644 lib/silex/vendor/twig/twig/src/Extension/OptimizerExtension.php create mode 100644 lib/silex/vendor/twig/twig/src/Extension/ProfilerExtension.php create mode 100644 lib/silex/vendor/twig/twig/src/Extension/RuntimeExtensionInterface.php create mode 100644 lib/silex/vendor/twig/twig/src/Extension/SandboxExtension.php create mode 100644 lib/silex/vendor/twig/twig/src/Extension/StagingExtension.php create mode 100644 lib/silex/vendor/twig/twig/src/Extension/StringLoaderExtension.php create mode 100644 lib/silex/vendor/twig/twig/src/FileExtensionEscapingStrategy.php create mode 100644 lib/silex/vendor/twig/twig/src/Lexer.php create mode 100644 lib/silex/vendor/twig/twig/src/Loader/ArrayLoader.php create mode 100644 lib/silex/vendor/twig/twig/src/Loader/ChainLoader.php create mode 100644 lib/silex/vendor/twig/twig/src/Loader/ExistsLoaderInterface.php create mode 100644 lib/silex/vendor/twig/twig/src/Loader/FilesystemLoader.php create mode 100644 lib/silex/vendor/twig/twig/src/Loader/LoaderInterface.php create mode 100644 lib/silex/vendor/twig/twig/src/Loader/SourceContextLoaderInterface.php create mode 100644 lib/silex/vendor/twig/twig/src/Markup.php create mode 100644 lib/silex/vendor/twig/twig/src/Node/AutoEscapeNode.php create mode 100644 lib/silex/vendor/twig/twig/src/Node/BlockNode.php create mode 100644 lib/silex/vendor/twig/twig/src/Node/BlockReferenceNode.php create mode 100644 lib/silex/vendor/twig/twig/src/Node/BodyNode.php create mode 100644 lib/silex/vendor/twig/twig/src/Node/CheckSecurityNode.php create mode 100644 lib/silex/vendor/twig/twig/src/Node/DoNode.php create mode 100644 lib/silex/vendor/twig/twig/src/Node/EmbedNode.php create mode 100644 lib/silex/vendor/twig/twig/src/Node/Expression/AbstractExpression.php create mode 100644 lib/silex/vendor/twig/twig/src/Node/Expression/ArrayExpression.php create mode 100644 lib/silex/vendor/twig/twig/src/Node/Expression/AssignNameExpression.php create mode 100644 lib/silex/vendor/twig/twig/src/Node/Expression/Binary/AbstractBinary.php create mode 100644 lib/silex/vendor/twig/twig/src/Node/Expression/Binary/AddBinary.php create mode 100644 lib/silex/vendor/twig/twig/src/Node/Expression/Binary/AndBinary.php create mode 100644 lib/silex/vendor/twig/twig/src/Node/Expression/Binary/BitwiseAndBinary.php create mode 100644 lib/silex/vendor/twig/twig/src/Node/Expression/Binary/BitwiseOrBinary.php create mode 100644 lib/silex/vendor/twig/twig/src/Node/Expression/Binary/BitwiseXorBinary.php create mode 100644 lib/silex/vendor/twig/twig/src/Node/Expression/Binary/ConcatBinary.php create mode 100644 lib/silex/vendor/twig/twig/src/Node/Expression/Binary/DivBinary.php create mode 100644 lib/silex/vendor/twig/twig/src/Node/Expression/Binary/EndsWithBinary.php create mode 100644 lib/silex/vendor/twig/twig/src/Node/Expression/Binary/EqualBinary.php create mode 100644 lib/silex/vendor/twig/twig/src/Node/Expression/Binary/FloorDivBinary.php create mode 100644 lib/silex/vendor/twig/twig/src/Node/Expression/Binary/GreaterBinary.php create mode 100644 lib/silex/vendor/twig/twig/src/Node/Expression/Binary/GreaterEqualBinary.php create mode 100644 lib/silex/vendor/twig/twig/src/Node/Expression/Binary/InBinary.php create mode 100644 lib/silex/vendor/twig/twig/src/Node/Expression/Binary/LessBinary.php create mode 100644 lib/silex/vendor/twig/twig/src/Node/Expression/Binary/LessEqualBinary.php create mode 100644 lib/silex/vendor/twig/twig/src/Node/Expression/Binary/MatchesBinary.php create mode 100644 lib/silex/vendor/twig/twig/src/Node/Expression/Binary/ModBinary.php create mode 100644 lib/silex/vendor/twig/twig/src/Node/Expression/Binary/MulBinary.php create mode 100644 lib/silex/vendor/twig/twig/src/Node/Expression/Binary/NotEqualBinary.php create mode 100644 lib/silex/vendor/twig/twig/src/Node/Expression/Binary/NotInBinary.php create mode 100644 lib/silex/vendor/twig/twig/src/Node/Expression/Binary/OrBinary.php create mode 100644 lib/silex/vendor/twig/twig/src/Node/Expression/Binary/PowerBinary.php create mode 100644 lib/silex/vendor/twig/twig/src/Node/Expression/Binary/RangeBinary.php create mode 100644 lib/silex/vendor/twig/twig/src/Node/Expression/Binary/StartsWithBinary.php create mode 100644 lib/silex/vendor/twig/twig/src/Node/Expression/Binary/SubBinary.php create mode 100644 lib/silex/vendor/twig/twig/src/Node/Expression/BlockReferenceExpression.php create mode 100644 lib/silex/vendor/twig/twig/src/Node/Expression/CallExpression.php create mode 100644 lib/silex/vendor/twig/twig/src/Node/Expression/ConditionalExpression.php create mode 100644 lib/silex/vendor/twig/twig/src/Node/Expression/ConstantExpression.php create mode 100644 lib/silex/vendor/twig/twig/src/Node/Expression/Filter/DefaultFilter.php create mode 100644 lib/silex/vendor/twig/twig/src/Node/Expression/FilterExpression.php create mode 100644 lib/silex/vendor/twig/twig/src/Node/Expression/FunctionExpression.php create mode 100644 lib/silex/vendor/twig/twig/src/Node/Expression/GetAttrExpression.php create mode 100644 lib/silex/vendor/twig/twig/src/Node/Expression/MethodCallExpression.php create mode 100644 lib/silex/vendor/twig/twig/src/Node/Expression/NameExpression.php create mode 100644 lib/silex/vendor/twig/twig/src/Node/Expression/NullCoalesceExpression.php create mode 100644 lib/silex/vendor/twig/twig/src/Node/Expression/ParentExpression.php create mode 100644 lib/silex/vendor/twig/twig/src/Node/Expression/TempNameExpression.php create mode 100644 lib/silex/vendor/twig/twig/src/Node/Expression/Test/ConstantTest.php create mode 100644 lib/silex/vendor/twig/twig/src/Node/Expression/Test/DefinedTest.php create mode 100644 lib/silex/vendor/twig/twig/src/Node/Expression/Test/DivisiblebyTest.php create mode 100644 lib/silex/vendor/twig/twig/src/Node/Expression/Test/EvenTest.php create mode 100644 lib/silex/vendor/twig/twig/src/Node/Expression/Test/NullTest.php create mode 100644 lib/silex/vendor/twig/twig/src/Node/Expression/Test/OddTest.php create mode 100644 lib/silex/vendor/twig/twig/src/Node/Expression/Test/SameasTest.php create mode 100644 lib/silex/vendor/twig/twig/src/Node/Expression/TestExpression.php create mode 100644 lib/silex/vendor/twig/twig/src/Node/Expression/Unary/AbstractUnary.php create mode 100644 lib/silex/vendor/twig/twig/src/Node/Expression/Unary/NegUnary.php create mode 100644 lib/silex/vendor/twig/twig/src/Node/Expression/Unary/NotUnary.php create mode 100644 lib/silex/vendor/twig/twig/src/Node/Expression/Unary/PosUnary.php create mode 100644 lib/silex/vendor/twig/twig/src/Node/FlushNode.php create mode 100644 lib/silex/vendor/twig/twig/src/Node/ForLoopNode.php create mode 100644 lib/silex/vendor/twig/twig/src/Node/ForNode.php create mode 100644 lib/silex/vendor/twig/twig/src/Node/IfNode.php create mode 100644 lib/silex/vendor/twig/twig/src/Node/ImportNode.php create mode 100644 lib/silex/vendor/twig/twig/src/Node/IncludeNode.php create mode 100644 lib/silex/vendor/twig/twig/src/Node/MacroNode.php create mode 100644 lib/silex/vendor/twig/twig/src/Node/ModuleNode.php create mode 100644 lib/silex/vendor/twig/twig/src/Node/Node.php create mode 100644 lib/silex/vendor/twig/twig/src/Node/NodeCaptureInterface.php create mode 100644 lib/silex/vendor/twig/twig/src/Node/NodeOutputInterface.php create mode 100644 lib/silex/vendor/twig/twig/src/Node/PrintNode.php create mode 100644 lib/silex/vendor/twig/twig/src/Node/SandboxNode.php create mode 100644 lib/silex/vendor/twig/twig/src/Node/SandboxedPrintNode.php create mode 100644 lib/silex/vendor/twig/twig/src/Node/SetNode.php create mode 100644 lib/silex/vendor/twig/twig/src/Node/SetTempNode.php create mode 100644 lib/silex/vendor/twig/twig/src/Node/SpacelessNode.php create mode 100644 lib/silex/vendor/twig/twig/src/Node/TextNode.php create mode 100644 lib/silex/vendor/twig/twig/src/Node/WithNode.php create mode 100644 lib/silex/vendor/twig/twig/src/NodeTraverser.php create mode 100644 lib/silex/vendor/twig/twig/src/NodeVisitor/AbstractNodeVisitor.php create mode 100644 lib/silex/vendor/twig/twig/src/NodeVisitor/EscaperNodeVisitor.php create mode 100644 lib/silex/vendor/twig/twig/src/NodeVisitor/NodeVisitorInterface.php create mode 100644 lib/silex/vendor/twig/twig/src/NodeVisitor/OptimizerNodeVisitor.php create mode 100644 lib/silex/vendor/twig/twig/src/NodeVisitor/SafeAnalysisNodeVisitor.php create mode 100644 lib/silex/vendor/twig/twig/src/NodeVisitor/SandboxNodeVisitor.php create mode 100644 lib/silex/vendor/twig/twig/src/Parser.php create mode 100644 lib/silex/vendor/twig/twig/src/Profiler/Dumper/BaseDumper.php create mode 100644 lib/silex/vendor/twig/twig/src/Profiler/Dumper/BlackfireDumper.php create mode 100644 lib/silex/vendor/twig/twig/src/Profiler/Dumper/HtmlDumper.php create mode 100644 lib/silex/vendor/twig/twig/src/Profiler/Dumper/TextDumper.php create mode 100644 lib/silex/vendor/twig/twig/src/Profiler/Node/EnterProfileNode.php create mode 100644 lib/silex/vendor/twig/twig/src/Profiler/Node/LeaveProfileNode.php create mode 100644 lib/silex/vendor/twig/twig/src/Profiler/NodeVisitor/ProfilerNodeVisitor.php create mode 100644 lib/silex/vendor/twig/twig/src/Profiler/Profile.php create mode 100644 lib/silex/vendor/twig/twig/src/RuntimeLoader/ContainerRuntimeLoader.php create mode 100644 lib/silex/vendor/twig/twig/src/RuntimeLoader/FactoryRuntimeLoader.php create mode 100644 lib/silex/vendor/twig/twig/src/RuntimeLoader/RuntimeLoaderInterface.php create mode 100644 lib/silex/vendor/twig/twig/src/Sandbox/SecurityError.php create mode 100644 lib/silex/vendor/twig/twig/src/Sandbox/SecurityNotAllowedFilterError.php create mode 100644 lib/silex/vendor/twig/twig/src/Sandbox/SecurityNotAllowedFunctionError.php create mode 100644 lib/silex/vendor/twig/twig/src/Sandbox/SecurityNotAllowedMethodError.php create mode 100644 lib/silex/vendor/twig/twig/src/Sandbox/SecurityNotAllowedPropertyError.php create mode 100644 lib/silex/vendor/twig/twig/src/Sandbox/SecurityNotAllowedTagError.php create mode 100644 lib/silex/vendor/twig/twig/src/Sandbox/SecurityPolicy.php create mode 100644 lib/silex/vendor/twig/twig/src/Sandbox/SecurityPolicyInterface.php create mode 100644 lib/silex/vendor/twig/twig/src/Source.php create mode 100644 lib/silex/vendor/twig/twig/src/Template.php create mode 100644 lib/silex/vendor/twig/twig/src/TemplateWrapper.php create mode 100644 lib/silex/vendor/twig/twig/src/Test/IntegrationTestCase.php create mode 100644 lib/silex/vendor/twig/twig/src/Test/NodeTestCase.php create mode 100644 lib/silex/vendor/twig/twig/src/Token.php create mode 100644 lib/silex/vendor/twig/twig/src/TokenParser/AbstractTokenParser.php create mode 100644 lib/silex/vendor/twig/twig/src/TokenParser/AutoEscapeTokenParser.php create mode 100644 lib/silex/vendor/twig/twig/src/TokenParser/BlockTokenParser.php create mode 100644 lib/silex/vendor/twig/twig/src/TokenParser/DoTokenParser.php create mode 100644 lib/silex/vendor/twig/twig/src/TokenParser/EmbedTokenParser.php create mode 100644 lib/silex/vendor/twig/twig/src/TokenParser/ExtendsTokenParser.php create mode 100644 lib/silex/vendor/twig/twig/src/TokenParser/FilterTokenParser.php create mode 100644 lib/silex/vendor/twig/twig/src/TokenParser/FlushTokenParser.php create mode 100644 lib/silex/vendor/twig/twig/src/TokenParser/ForTokenParser.php create mode 100644 lib/silex/vendor/twig/twig/src/TokenParser/FromTokenParser.php create mode 100644 lib/silex/vendor/twig/twig/src/TokenParser/IfTokenParser.php create mode 100644 lib/silex/vendor/twig/twig/src/TokenParser/ImportTokenParser.php create mode 100644 lib/silex/vendor/twig/twig/src/TokenParser/IncludeTokenParser.php create mode 100644 lib/silex/vendor/twig/twig/src/TokenParser/MacroTokenParser.php create mode 100644 lib/silex/vendor/twig/twig/src/TokenParser/SandboxTokenParser.php create mode 100644 lib/silex/vendor/twig/twig/src/TokenParser/SetTokenParser.php create mode 100644 lib/silex/vendor/twig/twig/src/TokenParser/SpacelessTokenParser.php create mode 100644 lib/silex/vendor/twig/twig/src/TokenParser/TokenParserInterface.php create mode 100644 lib/silex/vendor/twig/twig/src/TokenParser/UseTokenParser.php create mode 100644 lib/silex/vendor/twig/twig/src/TokenParser/WithTokenParser.php create mode 100644 lib/silex/vendor/twig/twig/src/TokenStream.php create mode 100644 lib/silex/vendor/twig/twig/src/TwigFilter.php create mode 100644 lib/silex/vendor/twig/twig/src/TwigFunction.php create mode 100644 lib/silex/vendor/twig/twig/src/TwigTest.php create mode 100644 lib/silex/vendor/twig/twig/src/Util/DeprecationCollector.php create mode 100644 lib/silex/vendor/twig/twig/src/Util/TemplateDirIterator.php create mode 100644 lib/silex/vendor/twig/twig/test/Twig/Tests/Cache/FilesystemTest.php create mode 100644 lib/silex/vendor/twig/twig/test/Twig/Tests/ContainerRuntimeLoaderTest.php create mode 100644 lib/silex/vendor/twig/twig/test/Twig/Tests/CustomExtensionTest.php create mode 100644 lib/silex/vendor/twig/twig/test/Twig/Tests/FactoryRuntimeLoaderTest.php create mode 100644 lib/silex/vendor/twig/twig/test/Twig/Tests/FilesystemHelper.php create mode 100644 lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/autoescape/block.test create mode 100644 lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/autoescape/name.test create mode 100644 lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/exceptions/child_contents_outside_blocks.test create mode 100644 lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/expressions/_self.test create mode 100644 lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/expressions/power.test create mode 100644 lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/escape_javascript.test create mode 100644 lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/static_calls.test create mode 100644 lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/functions/block_with_template.test create mode 100644 lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/functions/block_without_name.test create mode 100644 lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/functions/magic_call.test create mode 100644 lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/functions/magic_call53.test create mode 100644 lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/functions/static_calls.test create mode 100644 lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/embed/complex_dynamic_parent.test create mode 100644 lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/embed/dynamic_parent.test create mode 100644 lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/use/use_with_parent.test create mode 100644 lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/with/basic.test create mode 100644 lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/with/expression.test create mode 100644 lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/with/nested.test create mode 100644 lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/with/with_no_hash.test create mode 100644 lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/with/with_only.test create mode 100644 lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/tests/defined_for_attribute.test create mode 100644 lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/tests/defined_for_blocks.test create mode 100644 lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/tests/defined_for_blocks_with_template.test create mode 100644 lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/tests/defined_for_constants.test create mode 100644 lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/tests/null_coalesce.test create mode 100644 lib/silex/vendor/twig/twig/test/Twig/Tests/LegacyFixtures/autoescape/filename.legacy.test create mode 100644 lib/silex/vendor/twig/twig/test/Twig/Tests/LegacyFixtures/functions/undefined_block.legacy.test create mode 100644 lib/silex/vendor/twig/twig/test/Twig/Tests/Loader/Fixtures/phar/phar-sample.phar create mode 100644 lib/silex/vendor/twig/twig/test/Twig/Tests/Node/Expression/NullCoalesceTest.php create mode 100644 lib/silex/vendor/twig/twig/test/Twig/Tests/TemplateWrapperTest.php create mode 100644 lib/silex/vendor/twig/twig/test/Twig/Tests/Util/DeprecationCollectorTest.php diff --git a/datamodels/2.x/itop-portal-base/portal/src/forms/objectformmanager.class.inc.php b/datamodels/2.x/itop-portal-base/portal/src/forms/objectformmanager.class.inc.php index c3936d5d7..20114f57b 100644 --- a/datamodels/2.x/itop-portal-base/portal/src/forms/objectformmanager.class.inc.php +++ b/datamodels/2.x/itop-portal-base/portal/src/forms/objectformmanager.class.inc.php @@ -377,9 +377,9 @@ class ObjectFormManager extends FormManager if ($this->aFormProperties['layout']['type'] === 'twig') { // Creating sandbox twig env. to load and test the custom form template - $oTwig = new \Twig_Environment(new \Twig_Loader_String()); + $oTwig = new \Twig_Environment(new \Twig_Loader_Array( array($oForm->GetId() => $this->aFormProperties['layout']['content']) )); ApplicationHelper::RegisterTwigExtensions($oTwig); - $sRendered = $oTwig->render($this->aFormProperties['layout']['content'], array('oRenderer' => $this->oRenderer, 'oObject' => $this->oObject)); + $sRendered = $oTwig->render($oForm->GetId(), array('oRenderer' => $this->oRenderer, 'oObject' => $this->oObject)); } else { diff --git a/datamodels/2.x/itop-portal-base/portal/src/helpers/applicationhelper.class.inc.php b/datamodels/2.x/itop-portal-base/portal/src/helpers/applicationhelper.class.inc.php index 00116050f..493f9666f 100644 --- a/datamodels/2.x/itop-portal-base/portal/src/helpers/applicationhelper.class.inc.php +++ b/datamodels/2.x/itop-portal-base/portal/src/helpers/applicationhelper.class.inc.php @@ -23,6 +23,8 @@ use Exception; use Silex\Application; use Symfony\Component\Debug\ErrorHandler; use Symfony\Component\Debug\ExceptionHandler; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpKernel\Exception\HttpException; use Twig_Environment; use Twig_SimpleFilter; use Dict; @@ -277,16 +279,18 @@ class ApplicationHelper // Intercepting manually aborted request if (1 || !$oApp['debug']) { - $oApp->error(function(Exception $e, $code) use ($oApp) + $oApp->error(function(Exception $oException, Request $oRequest) use ($oApp) { + $iErrorCode = ($oException instanceof HttpException) ? $oException->getStatusCode() : 500; + $aData = array( - 'exception' => $e, - 'code' => $code, + 'exception' => $oException, + 'code' => $iErrorCode, 'error_title' => '', - 'error_message' => $e->getMessage() + 'error_message' => $oException->getMessage() ); - switch ($code) + switch ($iErrorCode) { case 404: $aData['error_title'] = Dict::S('Error:HTTP:404'); @@ -298,15 +302,15 @@ class ApplicationHelper IssueLog::Error($aData['error_title'] . ' : ' . $aData['error_message']); - if ($oApp['request']->isXmlHttpRequest()) + if ($oApp['request_stack']->getCurrentRequest()->isXmlHttpRequest()) { - $oResponse = $oApp->json($aData, $code); + $oResponse = $oApp->json($aData, $iErrorCode); } else { // Preparing debug trace $aSteps = array(); - foreach($e->getTrace() as $aStep) + foreach($oException->getTrace() as $aStep) { // - Default file name if(!isset($aStep['file'])) diff --git a/datamodels/2.x/itop-portal-base/portal/src/providers/contextmanipulatorserviceprovider.class.inc.php b/datamodels/2.x/itop-portal-base/portal/src/providers/contextmanipulatorserviceprovider.class.inc.php index b2f57e68a..5d780163b 100644 --- a/datamodels/2.x/itop-portal-base/portal/src/providers/contextmanipulatorserviceprovider.class.inc.php +++ b/datamodels/2.x/itop-portal-base/portal/src/providers/contextmanipulatorserviceprovider.class.inc.php @@ -19,8 +19,8 @@ namespace Combodo\iTop\Portal\Provider; -use Silex\Application; -use Silex\ServiceProviderInterface; +use Pimple\Container; +use Pimple\ServiceProviderInterface; use Combodo\iTop\Portal\Helper\ContextManipulatorHelper; /** @@ -31,9 +31,9 @@ use Combodo\iTop\Portal\Helper\ContextManipulatorHelper; class ContextManipulatorServiceProvider implements ServiceProviderInterface { - public function register(Application $oApp) + public function register(Container $oApp) { - $oApp['context_manipulator'] = $oApp->share(function ($oApp) + $oApp['context_manipulator'] = function ($oApp) { $oApp->flush(); @@ -41,10 +41,10 @@ class ContextManipulatorServiceProvider implements ServiceProviderInterface $oContextManipulatorHelper->SetApp($oApp); return $oContextManipulatorHelper; - }); + }; } - public function boot(Application $oApp) + public function boot(Container $oApp) { } diff --git a/datamodels/2.x/itop-portal-base/portal/src/providers/lifecyclevalidatorserviceprovider.class.inc.php b/datamodels/2.x/itop-portal-base/portal/src/providers/lifecyclevalidatorserviceprovider.class.inc.php index 28f322dca..e4ddbaab8 100644 --- a/datamodels/2.x/itop-portal-base/portal/src/providers/lifecyclevalidatorserviceprovider.class.inc.php +++ b/datamodels/2.x/itop-portal-base/portal/src/providers/lifecyclevalidatorserviceprovider.class.inc.php @@ -19,8 +19,8 @@ namespace Combodo\iTop\Portal\Provider; -use Silex\Application; -use Silex\ServiceProviderInterface; +use Pimple\Container; +use Pimple\ServiceProviderInterface; use Combodo\iTop\Portal\Helper\LifecycleValidatorHelper; /** @@ -31,9 +31,9 @@ use Combodo\iTop\Portal\Helper\LifecycleValidatorHelper; class LifecycleValidatorServiceProvider implements ServiceProviderInterface { - public function register(Application $oApp) + public function register(Container $oApp) { - $oApp['lifecycle_validator'] = $oApp->share(function ($oApp) + $oApp['lifecycle_validator'] = function ($oApp) { $oApp->flush(); @@ -44,10 +44,10 @@ class LifecycleValidatorServiceProvider implements ServiceProviderInterface } return $oLifecycleValidatorHelper; - }); + }; } - public function boot(Application $oApp) + public function boot(Container $oApp) { } diff --git a/datamodels/2.x/itop-portal-base/portal/src/providers/scopevalidatorserviceprovider.class.inc.php b/datamodels/2.x/itop-portal-base/portal/src/providers/scopevalidatorserviceprovider.class.inc.php index 565ed205a..bd406ad49 100644 --- a/datamodels/2.x/itop-portal-base/portal/src/providers/scopevalidatorserviceprovider.class.inc.php +++ b/datamodels/2.x/itop-portal-base/portal/src/providers/scopevalidatorserviceprovider.class.inc.php @@ -19,8 +19,8 @@ namespace Combodo\iTop\Portal\Provider; -use Silex\Application; -use Silex\ServiceProviderInterface; +use Pimple\Container; +use Pimple\ServiceProviderInterface; use Combodo\iTop\Portal\Helper\ScopeValidatorHelper; /** @@ -31,9 +31,9 @@ use Combodo\iTop\Portal\Helper\ScopeValidatorHelper; class ScopeValidatorServiceProvider implements ServiceProviderInterface { - public function register(Application $oApp) + public function register(Container $oApp) { - $oApp['scope_validator'] = $oApp->share(function ($oApp) + $oApp['scope_validator'] = function ($oApp) { $oApp->flush(); @@ -44,10 +44,10 @@ class ScopeValidatorServiceProvider implements ServiceProviderInterface } return $oScopeValidatorHelper; - }); + }; } - public function boot(Application $oApp) + public function boot(Container $oApp) { } diff --git a/datamodels/2.x/itop-portal-base/portal/src/providers/urlgeneratorserviceprovider.class.inc.php b/datamodels/2.x/itop-portal-base/portal/src/providers/urlgeneratorserviceprovider.class.inc.php index 1fe1a6f4a..05c69e485 100644 --- a/datamodels/2.x/itop-portal-base/portal/src/providers/urlgeneratorserviceprovider.class.inc.php +++ b/datamodels/2.x/itop-portal-base/portal/src/providers/urlgeneratorserviceprovider.class.inc.php @@ -19,8 +19,8 @@ namespace Combodo\iTop\Portal\Provider; -use Silex\Application; -use Silex\ServiceProviderInterface; +use Pimple\Container; +use Pimple\ServiceProviderInterface; use Combodo\iTop\Portal\Helper\UrlGenerator; /** @@ -31,17 +31,17 @@ use Combodo\iTop\Portal\Helper\UrlGenerator; class UrlGeneratorServiceProvider implements ServiceProviderInterface { - public function register(Application $oApp) + public function register(Container $oApp) { - $oApp['url_generator'] = $oApp->share(function ($oApp) + $oApp['url_generator'] = function ($oApp) { $oApp->flush(); return new UrlGenerator($oApp['routes'], $oApp['request_context']); - }); + }; } - public function boot(Application $oApp) + public function boot(Container $oApp) { } diff --git a/lib/silex/composer.json b/lib/silex/composer.json index 6f0847084..caf83ddd4 100644 --- a/lib/silex/composer.json +++ b/lib/silex/composer.json @@ -1,6 +1,6 @@ { "require": { - "silex/silex": "~1.1", - "twig/twig": "1.21.*" + "silex/silex": "~2.0", + "twig/twig": "1.35" } } diff --git a/lib/silex/composer.lock b/lib/silex/composer.lock index 4df5682ec..3a3bcde1b 100644 --- a/lib/silex/composer.lock +++ b/lib/silex/composer.lock @@ -4,35 +4,86 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "hash": "0cef8e455a5b99f76246934c54fb0b18", - "content-hash": "bcccad4b8962ec6793031384b3b2fc97", + "content-hash": "d3dddb99878fdc490eef79fde08c0b81", "packages": [ { - "name": "pimple/pimple", - "version": "v1.1.1", + "name": "paragonie/random_compat", + "version": "v2.0.11", "source": { "type": "git", - "url": "https://github.com/silexphp/Pimple.git", - "reference": "2019c145fe393923f3441b23f29bbdfaa5c58c4d" + "url": "https://github.com/paragonie/random_compat.git", + "reference": "5da4d3c796c275c55f057af5a643ae297d96b4d8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/silexphp/Pimple/zipball/2019c145fe393923f3441b23f29bbdfaa5c58c4d", - "reference": "2019c145fe393923f3441b23f29bbdfaa5c58c4d", + "url": "https://api.github.com/repos/paragonie/random_compat/zipball/5da4d3c796c275c55f057af5a643ae297d96b4d8", + "reference": "5da4d3c796c275c55f057af5a643ae297d96b4d8", "shasum": "" }, "require": { - "php": ">=5.3.0" + "php": ">=5.2.0" + }, + "require-dev": { + "phpunit/phpunit": "4.*|5.*" + }, + "suggest": { + "ext-libsodium": "Provides a modern crypto API that can be used to generate random bytes." + }, + "type": "library", + "autoload": { + "files": [ + "lib/random.php" + ] + }, + "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", + "pseudorandom", + "random" + ], + "time": "2017-09-27T21:40:39+00:00" + }, + { + "name": "pimple/pimple", + "version": "v3.2.3", + "source": { + "type": "git", + "url": "https://github.com/silexphp/Pimple.git", + "reference": "9e403941ef9d65d20cba7d54e29fe906db42cf32" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/silexphp/Pimple/zipball/9e403941ef9d65d20cba7d54e29fe906db42cf32", + "reference": "9e403941ef9d65d20cba7d54e29fe906db42cf32", + "shasum": "" + }, + "require": { + "php": ">=5.3.0", + "psr/container": "^1.0" + }, + "require-dev": { + "symfony/phpunit-bridge": "^3.2" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.1.x-dev" + "dev-master": "3.2.x-dev" } }, "autoload": { "psr-0": { - "Pimple": "lib/" + "Pimple": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -45,32 +96,89 @@ "email": "fabien@symfony.com" } ], - "description": "Pimple is a simple Dependency Injection Container for PHP 5.3", + "description": "Pimple, a simple Dependency Injection Container", "homepage": "http://pimple.sensiolabs.org", "keywords": [ "container", "dependency injection" ], - "time": "2013-11-22 08:30:29" + "time": "2018-01-21T07:42:36+00:00" }, { - "name": "psr/log", + "name": "psr/container", "version": "1.0.0", "source": { "type": "git", - "url": "https://github.com/php-fig/log.git", - "reference": "fe0936ee26643249e916849d48e3a51d5f5e278b" + "url": "https://github.com/php-fig/container.git", + "reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/log/zipball/fe0936ee26643249e916849d48e3a51d5f5e278b", - "reference": "fe0936ee26643249e916849d48e3a51d5f5e278b", + "url": "https://api.github.com/repos/php-fig/container/zipball/b7ce3b176482dbbc1245ebf52b181af44c2cf55f", + "reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f", "shasum": "" }, + "require": { + "php": ">=5.3.0" + }, "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, "autoload": { - "psr-0": { - "Psr\\Log\\": "" + "psr-4": { + "Psr\\Container\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common Container Interface (PHP FIG PSR-11)", + "homepage": "https://github.com/php-fig/container", + "keywords": [ + "PSR-11", + "container", + "container-interface", + "container-interop", + "psr" + ], + "time": "2017-02-14T16:28:37+00:00" + }, + { + "name": "psr/log", + "version": "1.0.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/log.git", + "reference": "4ebe3a8bf773a19edfe0a84b6585ba3d401b724d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/log/zipball/4ebe3a8bf773a19edfe0a84b6585ba3d401b724d", + "reference": "4ebe3a8bf773a19edfe0a84b6585ba3d401b724d", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Log\\": "Psr/Log/" } }, "notification-url": "https://packagist.org/downloads/", @@ -84,72 +192,80 @@ } ], "description": "Common interface for logging libraries", + "homepage": "https://github.com/php-fig/log", "keywords": [ "log", "psr", "psr-3" ], - "time": "2012-12-21 11:40:51" + "time": "2016-10-10T12:19:37+00:00" }, { "name": "silex/silex", - "version": "v1.2.5", + "version": "v2.2.2", "source": { "type": "git", "url": "https://github.com/silexphp/Silex.git", - "reference": "ce75b98d82d4c509802e63005c618392db17afef" + "reference": "3fe50331f194c83ded2f824c47c1c24c20877a8c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/silexphp/Silex/zipball/ce75b98d82d4c509802e63005c618392db17afef", - "reference": "ce75b98d82d4c509802e63005c618392db17afef", + "url": "https://api.github.com/repos/silexphp/Silex/zipball/3fe50331f194c83ded2f824c47c1c24c20877a8c", + "reference": "3fe50331f194c83ded2f824c47c1c24c20877a8c", "shasum": "" }, "require": { - "php": ">=5.3.3", - "pimple/pimple": "~1.0", - "symfony/event-dispatcher": "~2.3,<2.7", - "symfony/http-foundation": "~2.3,<2.7", - "symfony/http-kernel": "~2.3,<2.7", - "symfony/routing": "~2.3,<2.7" + "php": ">=5.5.9", + "pimple/pimple": "~3.0", + "symfony/event-dispatcher": "~2.8|^3.0", + "symfony/http-foundation": "~2.8|^3.0", + "symfony/http-kernel": "~2.8|^3.0", + "symfony/routing": "~2.8|^3.0" + }, + "conflict": { + "phpunit/phpunit": "<4.8.35 || >= 5.0, <5.4.3" + }, + "replace": { + "silex/api": "self.version", + "silex/providers": "self.version" }, "require-dev": { "doctrine/dbal": "~2.2", - "monolog/monolog": "~1.4,>=1.4.1", - "swiftmailer/swiftmailer": "5.*", - "symfony/browser-kit": "~2.3,<2.7", - "symfony/config": "~2.3,<2.7", - "symfony/css-selector": "~2.3,<2.7", - "symfony/debug": "~2.3,<2.7", - "symfony/dom-crawler": "~2.3,<2.7", - "symfony/finder": "~2.3,<2.7", - "symfony/form": "~2.3,<2.7", - "symfony/locale": "~2.3,<2.7", - "symfony/monolog-bridge": "~2.3,<2.7", - "symfony/options-resolver": "~2.3,<2.7", - "symfony/process": "~2.3,<2.7", - "symfony/security": "~2.3,<2.7", - "symfony/serializer": "~2.3,<2.7", - "symfony/translation": "~2.3,<2.7", - "symfony/twig-bridge": "~2.3,<2.7", - "symfony/validator": "~2.3,<2.7", - "twig/twig": ">=1.8.0,<2.0-dev" - }, - "suggest": { - "symfony/browser-kit": "~2.3", - "symfony/css-selector": "~2.3", - "symfony/dom-crawler": "~2.3", - "symfony/form": "~2.3" + "monolog/monolog": "^1.4.1", + "swiftmailer/swiftmailer": "~5", + "symfony/asset": "~2.8|^3.0", + "symfony/browser-kit": "~2.8|^3.0", + "symfony/config": "~2.8|^3.0", + "symfony/css-selector": "~2.8|^3.0", + "symfony/debug": "~2.8|^3.0", + "symfony/doctrine-bridge": "~2.8|^3.0", + "symfony/dom-crawler": "~2.8|^3.0", + "symfony/expression-language": "~2.8|^3.0", + "symfony/finder": "~2.8|^3.0", + "symfony/form": "~2.8|^3.0", + "symfony/intl": "~2.8|^3.0", + "symfony/monolog-bridge": "~2.8|^3.0", + "symfony/options-resolver": "~2.8|^3.0", + "symfony/phpunit-bridge": "^3.2", + "symfony/process": "~2.8|^3.0", + "symfony/security": "~2.8|^3.0", + "symfony/serializer": "~2.8|^3.0", + "symfony/translation": "~2.8|^3.0", + "symfony/twig-bridge": "~2.8|^3.0", + "symfony/validator": "~2.8|^3.0", + "symfony/var-dumper": "~2.8|^3.0", + "symfony/web-link": "^3.3", + "twig/twig": "~1.28|~2.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.2.x-dev" + "dev-master": "2.2.x-dev" } }, "autoload": { - "psr-0": { - "Silex": "src/" + "psr-4": { + "Silex\\": "src/Silex" } }, "notification-url": "https://packagist.org/downloads/", @@ -166,55 +282,50 @@ "email": "igor@wiedler.ch" } ], - "description": "The PHP micro-framework based on the Symfony2 Components", + "description": "The PHP micro-framework based on the Symfony Components", "homepage": "http://silex.sensiolabs.org", "keywords": [ "microframework" ], - "time": "2015-06-04 21:24:58" + "time": "2018-01-12T07:16:03+00:00" }, { "name": "symfony/debug", - "version": "v2.6.12", - "target-dir": "Symfony/Component/Debug", + "version": "v3.4.4", "source": { "type": "git", "url": "https://github.com/symfony/debug.git", - "reference": "fca5696e0c9787722baa8f2ad6940dfd7a6a6941" + "reference": "53f6af2805daf52a43b393b93d2f24925d35c937" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/debug/zipball/fca5696e0c9787722baa8f2ad6940dfd7a6a6941", - "reference": "fca5696e0c9787722baa8f2ad6940dfd7a6a6941", + "url": "https://api.github.com/repos/symfony/debug/zipball/53f6af2805daf52a43b393b93d2f24925d35c937", + "reference": "53f6af2805daf52a43b393b93d2f24925d35c937", "shasum": "" }, "require": { - "php": ">=5.3.3", + "php": "^5.5.9|>=7.0.8", "psr/log": "~1.0" }, "conflict": { "symfony/http-kernel": ">=2.3,<2.3.24|~2.4.0|>=2.5,<2.5.9|>=2.6,<2.6.2" }, "require-dev": { - "symfony/class-loader": "~2.2", - "symfony/http-foundation": "~2.1", - "symfony/http-kernel": "~2.3.24|~2.5.9|~2.6,>=2.6.2", - "symfony/phpunit-bridge": "~2.7" - }, - "suggest": { - "symfony/http-foundation": "", - "symfony/http-kernel": "" + "symfony/http-kernel": "~2.8|~3.0|~4.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.6-dev" + "dev-master": "3.4-dev" } }, "autoload": { - "psr-0": { + "psr-4": { "Symfony\\Component\\Debug\\": "" - } + }, + "exclude-from-classmap": [ + "/Tests/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -232,33 +343,34 @@ ], "description": "Symfony Debug Component", "homepage": "https://symfony.com", - "time": "2015-07-08 05:59:48" + "time": "2018-01-18T22:16:57+00:00" }, { "name": "symfony/event-dispatcher", - "version": "v2.6.12", - "target-dir": "Symfony/Component/EventDispatcher", + "version": "v3.4.4", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "672593bc4b0043a0acf91903bb75a1c82d8f2e02" + "reference": "26b87b6bca8f8f797331a30b76fdae5342dc26ca" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/672593bc4b0043a0acf91903bb75a1c82d8f2e02", - "reference": "672593bc4b0043a0acf91903bb75a1c82d8f2e02", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/26b87b6bca8f8f797331a30b76fdae5342dc26ca", + "reference": "26b87b6bca8f8f797331a30b76fdae5342dc26ca", "shasum": "" }, "require": { - "php": ">=5.3.3" + "php": "^5.5.9|>=7.0.8" + }, + "conflict": { + "symfony/dependency-injection": "<3.3" }, "require-dev": { "psr/log": "~1.0", - "symfony/config": "~2.0,>=2.0.5", - "symfony/dependency-injection": "~2.6", - "symfony/expression-language": "~2.6", - "symfony/phpunit-bridge": "~2.7", - "symfony/stopwatch": "~2.3" + "symfony/config": "~2.8|~3.0|~4.0", + "symfony/dependency-injection": "~3.3|~4.0", + "symfony/expression-language": "~2.8|~3.0|~4.0", + "symfony/stopwatch": "~2.8|~3.0|~4.0" }, "suggest": { "symfony/dependency-injection": "", @@ -267,13 +379,16 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.6-dev" + "dev-master": "3.4-dev" } }, "autoload": { - "psr-0": { + "psr-4": { "Symfony\\Component\\EventDispatcher\\": "" - } + }, + "exclude-from-classmap": [ + "/Tests/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -291,42 +406,42 @@ ], "description": "Symfony EventDispatcher Component", "homepage": "https://symfony.com", - "time": "2015-05-02 15:18:45" + "time": "2018-01-03T07:37:34+00:00" }, { "name": "symfony/http-foundation", - "version": "v2.6.12", - "target-dir": "Symfony/Component/HttpFoundation", + "version": "v3.4.4", "source": { "type": "git", "url": "https://github.com/symfony/http-foundation.git", - "reference": "e8fd1b73ac1c3de1f76c73801ddf1a8ecb1c1c9c" + "reference": "8c39071ac9cc7e6d8dab1d556c990dc0d2cc3d30" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-foundation/zipball/e8fd1b73ac1c3de1f76c73801ddf1a8ecb1c1c9c", - "reference": "e8fd1b73ac1c3de1f76c73801ddf1a8ecb1c1c9c", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/8c39071ac9cc7e6d8dab1d556c990dc0d2cc3d30", + "reference": "8c39071ac9cc7e6d8dab1d556c990dc0d2cc3d30", "shasum": "" }, "require": { - "php": ">=5.3.3" + "php": "^5.5.9|>=7.0.8", + "symfony/polyfill-mbstring": "~1.1", + "symfony/polyfill-php70": "~1.6" }, "require-dev": { - "symfony/expression-language": "~2.4", - "symfony/phpunit-bridge": "~2.7" + "symfony/expression-language": "~2.8|~3.0|~4.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.6-dev" + "dev-master": "3.4-dev" } }, "autoload": { - "psr-0": { + "psr-4": { "Symfony\\Component\\HttpFoundation\\": "" }, - "classmap": [ - "Symfony/Component/HttpFoundation/Resources/stubs" + "exclude-from-classmap": [ + "/Tests/" ] }, "notification-url": "https://packagist.org/downloads/", @@ -345,51 +460,58 @@ ], "description": "Symfony HttpFoundation Component", "homepage": "https://symfony.com", - "time": "2015-07-22 10:08:40" + "time": "2018-01-29T09:03:43+00:00" }, { "name": "symfony/http-kernel", - "version": "v2.6.12", - "target-dir": "Symfony/Component/HttpKernel", + "version": "v3.4.4", "source": { "type": "git", "url": "https://github.com/symfony/http-kernel.git", - "reference": "498866a8ca0bcbcd3f3824b1520fa568ff7ca3b6" + "reference": "911d2e5dd4beb63caad9a72e43857de984301907" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-kernel/zipball/498866a8ca0bcbcd3f3824b1520fa568ff7ca3b6", - "reference": "498866a8ca0bcbcd3f3824b1520fa568ff7ca3b6", + "url": "https://api.github.com/repos/symfony/http-kernel/zipball/911d2e5dd4beb63caad9a72e43857de984301907", + "reference": "911d2e5dd4beb63caad9a72e43857de984301907", "shasum": "" }, "require": { - "php": ">=5.3.3", + "php": "^5.5.9|>=7.0.8", "psr/log": "~1.0", - "symfony/debug": "~2.6,>=2.6.2", - "symfony/event-dispatcher": "~2.6,>=2.6.7", - "symfony/http-foundation": "~2.5,>=2.5.4" + "symfony/debug": "~2.8|~3.0|~4.0", + "symfony/event-dispatcher": "~2.8|~3.0|~4.0", + "symfony/http-foundation": "^3.4.4|^4.0.4" + }, + "conflict": { + "symfony/config": "<2.8", + "symfony/dependency-injection": "<3.4", + "symfony/var-dumper": "<3.3", + "twig/twig": "<1.34|<2.4,>=2" + }, + "provide": { + "psr/log-implementation": "1.0" }, "require-dev": { - "symfony/browser-kit": "~2.3", - "symfony/class-loader": "~2.1", - "symfony/config": "~2.0,>=2.0.5", - "symfony/console": "~2.3", - "symfony/css-selector": "~2.0,>=2.0.5", - "symfony/dependency-injection": "~2.2", - "symfony/dom-crawler": "~2.0,>=2.0.5", - "symfony/expression-language": "~2.4", - "symfony/finder": "~2.0,>=2.0.5", - "symfony/phpunit-bridge": "~2.7", - "symfony/process": "~2.0,>=2.0.5", - "symfony/routing": "~2.2", - "symfony/stopwatch": "~2.3", - "symfony/templating": "~2.2", - "symfony/translation": "~2.0,>=2.0.5", - "symfony/var-dumper": "~2.6" + "psr/cache": "~1.0", + "symfony/browser-kit": "~2.8|~3.0|~4.0", + "symfony/class-loader": "~2.8|~3.0", + "symfony/config": "~2.8|~3.0|~4.0", + "symfony/console": "~2.8|~3.0|~4.0", + "symfony/css-selector": "~2.8|~3.0|~4.0", + "symfony/dependency-injection": "~3.4|~4.0", + "symfony/dom-crawler": "~2.8|~3.0|~4.0", + "symfony/expression-language": "~2.8|~3.0|~4.0", + "symfony/finder": "~2.8|~3.0|~4.0", + "symfony/process": "~2.8|~3.0|~4.0", + "symfony/routing": "~3.4|~4.0", + "symfony/stopwatch": "~2.8|~3.0|~4.0", + "symfony/templating": "~2.8|~3.0|~4.0", + "symfony/translation": "~2.8|~3.0|~4.0", + "symfony/var-dumper": "~3.3|~4.0" }, "suggest": { "symfony/browser-kit": "", - "symfony/class-loader": "", "symfony/config": "", "symfony/console": "", "symfony/dependency-injection": "", @@ -399,13 +521,16 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.6-dev" + "dev-master": "3.4-dev" } }, "autoload": { - "psr-0": { + "psr-4": { "Symfony\\Component\\HttpKernel\\": "" - } + }, + "exclude-from-classmap": [ + "/Tests/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -423,52 +548,179 @@ ], "description": "Symfony HttpKernel Component", "homepage": "https://symfony.com", - "time": "2015-11-23 11:37:53" + "time": "2018-01-29T12:29:46+00:00" }, { - "name": "symfony/routing", - "version": "v2.6.12", - "target-dir": "Symfony/Component/Routing", + "name": "symfony/polyfill-mbstring", + "version": "v1.6.0", "source": { "type": "git", - "url": "https://github.com/symfony/routing.git", - "reference": "0a1764d41bbb54f3864808c50569ac382b44d128" + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "2ec8b39c38cb16674bbf3fea2b6ce5bf117e1296" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/routing/zipball/0a1764d41bbb54f3864808c50569ac382b44d128", - "reference": "0a1764d41bbb54f3864808c50569ac382b44d128", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/2ec8b39c38cb16674bbf3fea2b6ce5bf117e1296", + "reference": "2ec8b39c38cb16674bbf3fea2b6ce5bf117e1296", "shasum": "" }, "require": { "php": ">=5.3.3" }, + "suggest": { + "ext-mbstring": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.6-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Mbstring\\": "" + }, + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for the Mbstring extension", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "mbstring", + "polyfill", + "portable", + "shim" + ], + "time": "2017-10-11T12:05:26+00:00" + }, + { + "name": "symfony/polyfill-php70", + "version": "v1.6.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php70.git", + "reference": "0442b9c0596610bd24ae7b5f0a6cdbbc16d9fcff" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php70/zipball/0442b9c0596610bd24ae7b5f0a6cdbbc16d9fcff", + "reference": "0442b9c0596610bd24ae7b5f0a6cdbbc16d9fcff", + "shasum": "" + }, + "require": { + "paragonie/random_compat": "~1.0|~2.0", + "php": ">=5.3.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.6-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Php70\\": "" + }, + "files": [ + "bootstrap.php" + ], + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 7.0+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "time": "2017-10-11T12:05:26+00:00" + }, + { + "name": "symfony/routing", + "version": "v3.4.4", + "source": { + "type": "git", + "url": "https://github.com/symfony/routing.git", + "reference": "235d01730d553a97732990588407eaf6779bb4b2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/routing/zipball/235d01730d553a97732990588407eaf6779bb4b2", + "reference": "235d01730d553a97732990588407eaf6779bb4b2", + "shasum": "" + }, + "require": { + "php": "^5.5.9|>=7.0.8" + }, + "conflict": { + "symfony/config": "<2.8", + "symfony/dependency-injection": "<3.3", + "symfony/yaml": "<3.4" + }, "require-dev": { "doctrine/annotations": "~1.0", "doctrine/common": "~2.2", "psr/log": "~1.0", - "symfony/config": "~2.2", - "symfony/expression-language": "~2.4", - "symfony/http-foundation": "~2.3", - "symfony/phpunit-bridge": "~2.7", - "symfony/yaml": "~2.0,>=2.0.5" + "symfony/config": "~2.8|~3.0|~4.0", + "symfony/dependency-injection": "~3.3|~4.0", + "symfony/expression-language": "~2.8|~3.0|~4.0", + "symfony/http-foundation": "~2.8|~3.0|~4.0", + "symfony/yaml": "~3.4|~4.0" }, "suggest": { "doctrine/annotations": "For using the annotation loader", "symfony/config": "For using the all-in-one router or any loader", + "symfony/dependency-injection": "For loading routes from a service", "symfony/expression-language": "For using expression matching", + "symfony/http-foundation": "For using a Symfony Request object", "symfony/yaml": "For using the YAML loader" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.6-dev" + "dev-master": "3.4-dev" } }, "autoload": { - "psr-0": { + "psr-4": { "Symfony\\Component\\Routing\\": "" - } + }, + "exclude-from-classmap": [ + "/Tests/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -492,38 +744,42 @@ "uri", "url" ], - "time": "2015-07-09 16:02:48" + "time": "2018-01-16T18:03:57+00:00" }, { "name": "twig/twig", - "version": "v1.21.2", + "version": "v1.35.0", "source": { "type": "git", "url": "https://github.com/twigphp/Twig.git", - "reference": "ddce1136beb8db29b9cd7dffa8ab518b978c9db3" + "reference": "daa657073e55b0a78cce8fdd22682fddecc6385f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/twigphp/Twig/zipball/ddce1136beb8db29b9cd7dffa8ab518b978c9db3", - "reference": "ddce1136beb8db29b9cd7dffa8ab518b978c9db3", + "url": "https://api.github.com/repos/twigphp/Twig/zipball/daa657073e55b0a78cce8fdd22682fddecc6385f", + "reference": "daa657073e55b0a78cce8fdd22682fddecc6385f", "shasum": "" }, "require": { - "php": ">=5.2.7" + "php": ">=5.3.3" }, "require-dev": { + "psr/container": "^1.0", "symfony/debug": "~2.7", - "symfony/phpunit-bridge": "~2.7" + "symfony/phpunit-bridge": "~3.3@dev" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.21-dev" + "dev-master": "1.35-dev" } }, "autoload": { "psr-0": { "Twig_": "lib/" + }, + "psr-4": { + "Twig\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -553,7 +809,7 @@ "keywords": [ "templating" ], - "time": "2015-09-09 05:28:51" + "time": "2017-09-27T18:06:46+00:00" } ], "packages-dev": [], diff --git a/lib/silex/vendor/autoload.php b/lib/silex/vendor/autoload.php index 39a3de2f4..ac73d8e52 100644 --- a/lib/silex/vendor/autoload.php +++ b/lib/silex/vendor/autoload.php @@ -2,6 +2,6 @@ // autoload.php @generated by Composer -require_once __DIR__ . '/composer' . '/autoload_real.php'; +require_once __DIR__ . '/composer/autoload_real.php'; -return ComposerAutoloaderInite16e7860e9352d90241324e1bba430e8::getLoader(); +return ComposerAutoloaderInitc52ed037323c1472e1ed6497110f13b1::getLoader(); diff --git a/lib/silex/vendor/composer/ClassLoader.php b/lib/silex/vendor/composer/ClassLoader.php index 5e1469e83..dc02dfb11 100644 --- a/lib/silex/vendor/composer/ClassLoader.php +++ b/lib/silex/vendor/composer/ClassLoader.php @@ -13,9 +13,7 @@ namespace Composer\Autoload; /** - * ClassLoader implements a PSR-0 class loader - * - * See https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-0.md + * ClassLoader implements a PSR-0, PSR-4 and classmap class loader. * * $loader = new \Composer\Autoload\ClassLoader(); * @@ -39,6 +37,8 @@ namespace Composer\Autoload; * * @author Fabien Potencier * @author Jordi Boggiano + * @see http://www.php-fig.org/psr/psr-0/ + * @see http://www.php-fig.org/psr/psr-4/ */ class ClassLoader { @@ -53,8 +53,9 @@ class ClassLoader private $useIncludePath = false; private $classMap = array(); - private $classMapAuthoritative = false; + private $missingClasses = array(); + private $apcuPrefix; public function getPrefixes() { @@ -147,7 +148,7 @@ class ClassLoader * appending or prepending to the ones previously set for this namespace. * * @param string $prefix The prefix/namespace, with trailing '\\' - * @param array|string $paths The PSR-0 base directories + * @param array|string $paths The PSR-4 base directories * @param bool $prepend Whether to prepend the directories * * @throws \InvalidArgumentException @@ -271,6 +272,26 @@ class ClassLoader return $this->classMapAuthoritative; } + /** + * APCu prefix to use to cache found/not-found classes, if the extension is enabled. + * + * @param string|null $apcuPrefix + */ + public function setApcuPrefix($apcuPrefix) + { + $this->apcuPrefix = function_exists('apcu_fetch') && ini_get('apc.enabled') ? $apcuPrefix : null; + } + + /** + * The APCu prefix in use, or null if APCu caching is not enabled. + * + * @return string|null + */ + public function getApcuPrefix() + { + return $this->apcuPrefix; + } + /** * Registers this instance as an autoloader. * @@ -313,29 +334,34 @@ class ClassLoader */ public function findFile($class) { - // work around for PHP 5.3.0 - 5.3.2 https://bugs.php.net/50731 - if ('\\' == $class[0]) { - $class = substr($class, 1); - } - // class map lookup if (isset($this->classMap[$class])) { return $this->classMap[$class]; } - if ($this->classMapAuthoritative) { + if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) { return false; } + if (null !== $this->apcuPrefix) { + $file = apcu_fetch($this->apcuPrefix.$class, $hit); + if ($hit) { + return $file; + } + } $file = $this->findFileWithExtension($class, '.php'); // Search for Hack files if we are running on HHVM - if ($file === null && defined('HHVM_VERSION')) { + if (false === $file && defined('HHVM_VERSION')) { $file = $this->findFileWithExtension($class, '.hh'); } - if ($file === null) { + if (null !== $this->apcuPrefix) { + apcu_add($this->apcuPrefix.$class, $file); + } + + if (false === $file) { // Remember that this class does not exist. - return $this->classMap[$class] = false; + $this->missingClasses[$class] = true; } return $file; @@ -348,10 +374,14 @@ class ClassLoader $first = $class[0]; if (isset($this->prefixLengthsPsr4[$first])) { - foreach ($this->prefixLengthsPsr4[$first] as $prefix => $length) { - if (0 === strpos($class, $prefix)) { - foreach ($this->prefixDirsPsr4[$prefix] as $dir) { - if (file_exists($file = $dir . DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $length))) { + $subPath = $class; + while (false !== $lastPos = strrpos($subPath, '\\')) { + $subPath = substr($subPath, 0, $lastPos); + $search = $subPath.'\\'; + if (isset($this->prefixDirsPsr4[$search])) { + $pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1); + foreach ($this->prefixDirsPsr4[$search] as $dir) { + if (file_exists($file = $dir . $pathEnd)) { return $file; } } @@ -399,6 +429,8 @@ class ClassLoader if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) { return $file; } + + return false; } } diff --git a/lib/silex/vendor/composer/LICENSE b/lib/silex/vendor/composer/LICENSE index c8d57af8b..f27399a04 100644 --- a/lib/silex/vendor/composer/LICENSE +++ b/lib/silex/vendor/composer/LICENSE @@ -1,5 +1,5 @@ -Copyright (c) 2015 Nils Adermann, Jordi Boggiano +Copyright (c) Nils Adermann, Jordi Boggiano 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/lib/silex/vendor/composer/autoload_classmap.php b/lib/silex/vendor/composer/autoload_classmap.php index 2cc2fd2c0..3a1416586 100644 --- a/lib/silex/vendor/composer/autoload_classmap.php +++ b/lib/silex/vendor/composer/autoload_classmap.php @@ -6,5 +6,11 @@ $vendorDir = dirname(dirname(__FILE__)); $baseDir = dirname($vendorDir); return array( - 'SessionHandlerInterface' => $vendorDir . '/symfony/http-foundation/Symfony/Component/HttpFoundation/Resources/stubs/SessionHandlerInterface.php', + '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/lib/silex/vendor/composer/autoload_files.php b/lib/silex/vendor/composer/autoload_files.php new file mode 100644 index 000000000..776c6b55f --- /dev/null +++ b/lib/silex/vendor/composer/autoload_files.php @@ -0,0 +1,12 @@ + $vendorDir . '/paragonie/random_compat/lib/random.php', + '0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => $vendorDir . '/symfony/polyfill-mbstring/bootstrap.php', + '023d27dca8066ef29e6739335ea73bad' => $vendorDir . '/symfony/polyfill-php70/bootstrap.php', +); diff --git a/lib/silex/vendor/composer/autoload_namespaces.php b/lib/silex/vendor/composer/autoload_namespaces.php index 9bab75c71..213236c48 100644 --- a/lib/silex/vendor/composer/autoload_namespaces.php +++ b/lib/silex/vendor/composer/autoload_namespaces.php @@ -7,12 +7,5 @@ $baseDir = dirname($vendorDir); return array( 'Twig_' => array($vendorDir . '/twig/twig/lib'), - 'Symfony\\Component\\Routing\\' => array($vendorDir . '/symfony/routing'), - 'Symfony\\Component\\HttpKernel\\' => array($vendorDir . '/symfony/http-kernel'), - 'Symfony\\Component\\HttpFoundation\\' => array($vendorDir . '/symfony/http-foundation'), - 'Symfony\\Component\\EventDispatcher\\' => array($vendorDir . '/symfony/event-dispatcher'), - 'Symfony\\Component\\Debug\\' => array($vendorDir . '/symfony/debug'), - 'Silex' => array($vendorDir . '/silex/silex/src'), - 'Psr\\Log\\' => array($vendorDir . '/psr/log'), - 'Pimple' => array($vendorDir . '/pimple/pimple/lib'), + 'Pimple' => array($vendorDir . '/pimple/pimple/src'), ); diff --git a/lib/silex/vendor/composer/autoload_psr4.php b/lib/silex/vendor/composer/autoload_psr4.php index b265c64a2..dfd2e66ee 100644 --- a/lib/silex/vendor/composer/autoload_psr4.php +++ b/lib/silex/vendor/composer/autoload_psr4.php @@ -6,4 +6,15 @@ $vendorDir = dirname(dirname(__FILE__)); $baseDir = dirname($vendorDir); return array( + 'Twig\\' => array($vendorDir . '/twig/twig/src'), + 'Symfony\\Polyfill\\Php70\\' => array($vendorDir . '/symfony/polyfill-php70'), + 'Symfony\\Polyfill\\Mbstring\\' => array($vendorDir . '/symfony/polyfill-mbstring'), + 'Symfony\\Component\\Routing\\' => array($vendorDir . '/symfony/routing'), + 'Symfony\\Component\\HttpKernel\\' => array($vendorDir . '/symfony/http-kernel'), + 'Symfony\\Component\\HttpFoundation\\' => array($vendorDir . '/symfony/http-foundation'), + 'Symfony\\Component\\EventDispatcher\\' => array($vendorDir . '/symfony/event-dispatcher'), + 'Symfony\\Component\\Debug\\' => array($vendorDir . '/symfony/debug'), + 'Silex\\' => array($vendorDir . '/silex/silex/src/Silex'), + 'Psr\\Log\\' => array($vendorDir . '/psr/log/Psr/Log'), + 'Psr\\Container\\' => array($vendorDir . '/psr/container/src'), ); diff --git a/lib/silex/vendor/composer/autoload_real.php b/lib/silex/vendor/composer/autoload_real.php index 94ab13b2b..efc522d51 100644 --- a/lib/silex/vendor/composer/autoload_real.php +++ b/lib/silex/vendor/composer/autoload_real.php @@ -2,7 +2,7 @@ // autoload_real.php @generated by Composer -class ComposerAutoloaderInite16e7860e9352d90241324e1bba430e8 +class ComposerAutoloaderInitc52ed037323c1472e1ed6497110f13b1 { private static $loader; @@ -19,32 +19,52 @@ class ComposerAutoloaderInite16e7860e9352d90241324e1bba430e8 return self::$loader; } - spl_autoload_register(array('ComposerAutoloaderInite16e7860e9352d90241324e1bba430e8', 'loadClassLoader'), true, true); + spl_autoload_register(array('ComposerAutoloaderInitc52ed037323c1472e1ed6497110f13b1', 'loadClassLoader'), true, true); self::$loader = $loader = new \Composer\Autoload\ClassLoader(); - spl_autoload_unregister(array('ComposerAutoloaderInite16e7860e9352d90241324e1bba430e8', 'loadClassLoader')); + spl_autoload_unregister(array('ComposerAutoloaderInitc52ed037323c1472e1ed6497110f13b1', 'loadClassLoader')); - $map = require __DIR__ . '/autoload_namespaces.php'; - foreach ($map as $namespace => $path) { - $loader->set($namespace, $path); - } + $useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded()); + if ($useStaticLoader) { + require_once __DIR__ . '/autoload_static.php'; - $map = require __DIR__ . '/autoload_psr4.php'; - foreach ($map as $namespace => $path) { - $loader->setPsr4($namespace, $path); - } + call_user_func(\Composer\Autoload\ComposerStaticInitc52ed037323c1472e1ed6497110f13b1::getInitializer($loader)); + } else { + $map = require __DIR__ . '/autoload_namespaces.php'; + foreach ($map as $namespace => $path) { + $loader->set($namespace, $path); + } - $classMap = require __DIR__ . '/autoload_classmap.php'; - if ($classMap) { - $loader->addClassMap($classMap); + $map = require __DIR__ . '/autoload_psr4.php'; + foreach ($map as $namespace => $path) { + $loader->setPsr4($namespace, $path); + } + + $classMap = require __DIR__ . '/autoload_classmap.php'; + if ($classMap) { + $loader->addClassMap($classMap); + } } $loader->register(true); + if ($useStaticLoader) { + $includeFiles = Composer\Autoload\ComposerStaticInitc52ed037323c1472e1ed6497110f13b1::$files; + } else { + $includeFiles = require __DIR__ . '/autoload_files.php'; + } + foreach ($includeFiles as $fileIdentifier => $file) { + composerRequirec52ed037323c1472e1ed6497110f13b1($fileIdentifier, $file); + } + return $loader; } } -function composerRequiree16e7860e9352d90241324e1bba430e8($file) +function composerRequirec52ed037323c1472e1ed6497110f13b1($fileIdentifier, $file) { - require $file; + if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) { + require $file; + + $GLOBALS['__composer_autoload_files'][$fileIdentifier] = true; + } } diff --git a/lib/silex/vendor/composer/autoload_static.php b/lib/silex/vendor/composer/autoload_static.php new file mode 100644 index 000000000..3c1511d96 --- /dev/null +++ b/lib/silex/vendor/composer/autoload_static.php @@ -0,0 +1,122 @@ + __DIR__ . '/..' . '/paragonie/random_compat/lib/random.php', + '0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => __DIR__ . '/..' . '/symfony/polyfill-mbstring/bootstrap.php', + '023d27dca8066ef29e6739335ea73bad' => __DIR__ . '/..' . '/symfony/polyfill-php70/bootstrap.php', + ); + + public static $prefixLengthsPsr4 = array ( + 'T' => + array ( + 'Twig\\' => 5, + ), + 'S' => + array ( + 'Symfony\\Polyfill\\Php70\\' => 23, + 'Symfony\\Polyfill\\Mbstring\\' => 26, + 'Symfony\\Component\\Routing\\' => 26, + 'Symfony\\Component\\HttpKernel\\' => 29, + 'Symfony\\Component\\HttpFoundation\\' => 33, + 'Symfony\\Component\\EventDispatcher\\' => 34, + 'Symfony\\Component\\Debug\\' => 24, + 'Silex\\' => 6, + ), + 'P' => + array ( + 'Psr\\Log\\' => 8, + 'Psr\\Container\\' => 14, + ), + ); + + public static $prefixDirsPsr4 = array ( + 'Twig\\' => + array ( + 0 => __DIR__ . '/..' . '/twig/twig/src', + ), + 'Symfony\\Polyfill\\Php70\\' => + array ( + 0 => __DIR__ . '/..' . '/symfony/polyfill-php70', + ), + 'Symfony\\Polyfill\\Mbstring\\' => + array ( + 0 => __DIR__ . '/..' . '/symfony/polyfill-mbstring', + ), + 'Symfony\\Component\\Routing\\' => + array ( + 0 => __DIR__ . '/..' . '/symfony/routing', + ), + 'Symfony\\Component\\HttpKernel\\' => + array ( + 0 => __DIR__ . '/..' . '/symfony/http-kernel', + ), + 'Symfony\\Component\\HttpFoundation\\' => + array ( + 0 => __DIR__ . '/..' . '/symfony/http-foundation', + ), + 'Symfony\\Component\\EventDispatcher\\' => + array ( + 0 => __DIR__ . '/..' . '/symfony/event-dispatcher', + ), + 'Symfony\\Component\\Debug\\' => + array ( + 0 => __DIR__ . '/..' . '/symfony/debug', + ), + 'Silex\\' => + array ( + 0 => __DIR__ . '/..' . '/silex/silex/src/Silex', + ), + 'Psr\\Log\\' => + array ( + 0 => __DIR__ . '/..' . '/psr/log/Psr/Log', + ), + 'Psr\\Container\\' => + array ( + 0 => __DIR__ . '/..' . '/psr/container/src', + ), + ); + + public static $prefixesPsr0 = array ( + 'T' => + array ( + 'Twig_' => + array ( + 0 => __DIR__ . '/..' . '/twig/twig/lib', + ), + ), + 'P' => + array ( + 'Pimple' => + array ( + 0 => __DIR__ . '/..' . '/pimple/pimple/src', + ), + ), + ); + + 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 = ComposerStaticInitc52ed037323c1472e1ed6497110f13b1::$prefixLengthsPsr4; + $loader->prefixDirsPsr4 = ComposerStaticInitc52ed037323c1472e1ed6497110f13b1::$prefixDirsPsr4; + $loader->prefixesPsr0 = ComposerStaticInitc52ed037323c1472e1ed6497110f13b1::$prefixesPsr0; + $loader->classMap = ComposerStaticInitc52ed037323c1472e1ed6497110f13b1::$classMap; + + }, null, ClassLoader::class); + } +} diff --git a/lib/silex/vendor/composer/installed.json b/lib/silex/vendor/composer/installed.json index ef52e56fb..6098adaf5 100644 --- a/lib/silex/vendor/composer/installed.json +++ b/lib/silex/vendor/composer/installed.json @@ -1,25 +1,186 @@ [ { - "name": "psr/log", + "name": "paragonie/random_compat", + "version": "v2.0.11", + "version_normalized": "2.0.11.0", + "source": { + "type": "git", + "url": "https://github.com/paragonie/random_compat.git", + "reference": "5da4d3c796c275c55f057af5a643ae297d96b4d8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/paragonie/random_compat/zipball/5da4d3c796c275c55f057af5a643ae297d96b4d8", + "reference": "5da4d3c796c275c55f057af5a643ae297d96b4d8", + "shasum": "" + }, + "require": { + "php": ">=5.2.0" + }, + "require-dev": { + "phpunit/phpunit": "4.*|5.*" + }, + "suggest": { + "ext-libsodium": "Provides a modern crypto API that can be used to generate random bytes." + }, + "time": "2017-09-27T21:40:39+00:00", + "type": "library", + "installation-source": "dist", + "autoload": { + "files": [ + "lib/random.php" + ] + }, + "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", + "pseudorandom", + "random" + ] + }, + { + "name": "pimple/pimple", + "version": "v3.2.3", + "version_normalized": "3.2.3.0", + "source": { + "type": "git", + "url": "https://github.com/silexphp/Pimple.git", + "reference": "9e403941ef9d65d20cba7d54e29fe906db42cf32" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/silexphp/Pimple/zipball/9e403941ef9d65d20cba7d54e29fe906db42cf32", + "reference": "9e403941ef9d65d20cba7d54e29fe906db42cf32", + "shasum": "" + }, + "require": { + "php": ">=5.3.0", + "psr/container": "^1.0" + }, + "require-dev": { + "symfony/phpunit-bridge": "^3.2" + }, + "time": "2018-01-21T07:42:36+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.2.x-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-0": { + "Pimple": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + } + ], + "description": "Pimple, a simple Dependency Injection Container", + "homepage": "http://pimple.sensiolabs.org", + "keywords": [ + "container", + "dependency injection" + ] + }, + { + "name": "psr/container", "version": "1.0.0", "version_normalized": "1.0.0.0", "source": { "type": "git", - "url": "https://github.com/php-fig/log.git", - "reference": "fe0936ee26643249e916849d48e3a51d5f5e278b" + "url": "https://github.com/php-fig/container.git", + "reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/log/zipball/fe0936ee26643249e916849d48e3a51d5f5e278b", - "reference": "fe0936ee26643249e916849d48e3a51d5f5e278b", + "url": "https://api.github.com/repos/php-fig/container/zipball/b7ce3b176482dbbc1245ebf52b181af44c2cf55f", + "reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f", "shasum": "" }, - "time": "2012-12-21 11:40:51", + "require": { + "php": ">=5.3.0" + }, + "time": "2017-02-14T16:28:37+00:00", "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, "installation-source": "dist", "autoload": { - "psr-0": { - "Psr\\Log\\": "" + "psr-4": { + "Psr\\Container\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common Container Interface (PHP FIG PSR-11)", + "homepage": "https://github.com/php-fig/container", + "keywords": [ + "PSR-11", + "container", + "container-interface", + "container-interop", + "psr" + ] + }, + { + "name": "psr/log", + "version": "1.0.2", + "version_normalized": "1.0.2.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/log.git", + "reference": "4ebe3a8bf773a19edfe0a84b6585ba3d401b724d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/log/zipball/4ebe3a8bf773a19edfe0a84b6585ba3d401b724d", + "reference": "4ebe3a8bf773a19edfe0a84b6585ba3d401b724d", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "time": "2016-10-10T12:19:37+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Psr\\Log\\": "Psr/Log/" } }, "notification-url": "https://packagist.org/downloads/", @@ -33,122 +194,82 @@ } ], "description": "Common interface for logging libraries", + "homepage": "https://github.com/php-fig/log", "keywords": [ "log", "psr", "psr-3" ] }, - { - "name": "pimple/pimple", - "version": "v1.1.1", - "version_normalized": "1.1.1.0", - "source": { - "type": "git", - "url": "https://github.com/silexphp/Pimple.git", - "reference": "2019c145fe393923f3441b23f29bbdfaa5c58c4d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/silexphp/Pimple/zipball/2019c145fe393923f3441b23f29bbdfaa5c58c4d", - "reference": "2019c145fe393923f3441b23f29bbdfaa5c58c4d", - "shasum": "" - }, - "require": { - "php": ">=5.3.0" - }, - "time": "2013-11-22 08:30:29", - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.1.x-dev" - } - }, - "installation-source": "dist", - "autoload": { - "psr-0": { - "Pimple": "lib/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - } - ], - "description": "Pimple is a simple Dependency Injection Container for PHP 5.3", - "homepage": "http://pimple.sensiolabs.org", - "keywords": [ - "container", - "dependency injection" - ] - }, { "name": "silex/silex", - "version": "v1.2.5", - "version_normalized": "1.2.5.0", + "version": "v2.2.2", + "version_normalized": "2.2.2.0", "source": { "type": "git", "url": "https://github.com/silexphp/Silex.git", - "reference": "ce75b98d82d4c509802e63005c618392db17afef" + "reference": "3fe50331f194c83ded2f824c47c1c24c20877a8c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/silexphp/Silex/zipball/ce75b98d82d4c509802e63005c618392db17afef", - "reference": "ce75b98d82d4c509802e63005c618392db17afef", + "url": "https://api.github.com/repos/silexphp/Silex/zipball/3fe50331f194c83ded2f824c47c1c24c20877a8c", + "reference": "3fe50331f194c83ded2f824c47c1c24c20877a8c", "shasum": "" }, "require": { - "php": ">=5.3.3", - "pimple/pimple": "~1.0", - "symfony/event-dispatcher": "~2.3,<2.7", - "symfony/http-foundation": "~2.3,<2.7", - "symfony/http-kernel": "~2.3,<2.7", - "symfony/routing": "~2.3,<2.7" + "php": ">=5.5.9", + "pimple/pimple": "~3.0", + "symfony/event-dispatcher": "~2.8|^3.0", + "symfony/http-foundation": "~2.8|^3.0", + "symfony/http-kernel": "~2.8|^3.0", + "symfony/routing": "~2.8|^3.0" + }, + "conflict": { + "phpunit/phpunit": "<4.8.35 || >= 5.0, <5.4.3" + }, + "replace": { + "silex/api": "self.version", + "silex/providers": "self.version" }, "require-dev": { "doctrine/dbal": "~2.2", - "monolog/monolog": "~1.4,>=1.4.1", - "swiftmailer/swiftmailer": "5.*", - "symfony/browser-kit": "~2.3,<2.7", - "symfony/config": "~2.3,<2.7", - "symfony/css-selector": "~2.3,<2.7", - "symfony/debug": "~2.3,<2.7", - "symfony/dom-crawler": "~2.3,<2.7", - "symfony/finder": "~2.3,<2.7", - "symfony/form": "~2.3,<2.7", - "symfony/locale": "~2.3,<2.7", - "symfony/monolog-bridge": "~2.3,<2.7", - "symfony/options-resolver": "~2.3,<2.7", - "symfony/process": "~2.3,<2.7", - "symfony/security": "~2.3,<2.7", - "symfony/serializer": "~2.3,<2.7", - "symfony/translation": "~2.3,<2.7", - "symfony/twig-bridge": "~2.3,<2.7", - "symfony/validator": "~2.3,<2.7", - "twig/twig": ">=1.8.0,<2.0-dev" + "monolog/monolog": "^1.4.1", + "swiftmailer/swiftmailer": "~5", + "symfony/asset": "~2.8|^3.0", + "symfony/browser-kit": "~2.8|^3.0", + "symfony/config": "~2.8|^3.0", + "symfony/css-selector": "~2.8|^3.0", + "symfony/debug": "~2.8|^3.0", + "symfony/doctrine-bridge": "~2.8|^3.0", + "symfony/dom-crawler": "~2.8|^3.0", + "symfony/expression-language": "~2.8|^3.0", + "symfony/finder": "~2.8|^3.0", + "symfony/form": "~2.8|^3.0", + "symfony/intl": "~2.8|^3.0", + "symfony/monolog-bridge": "~2.8|^3.0", + "symfony/options-resolver": "~2.8|^3.0", + "symfony/phpunit-bridge": "^3.2", + "symfony/process": "~2.8|^3.0", + "symfony/security": "~2.8|^3.0", + "symfony/serializer": "~2.8|^3.0", + "symfony/translation": "~2.8|^3.0", + "symfony/twig-bridge": "~2.8|^3.0", + "symfony/validator": "~2.8|^3.0", + "symfony/var-dumper": "~2.8|^3.0", + "symfony/web-link": "^3.3", + "twig/twig": "~1.28|~2.0" }, - "suggest": { - "symfony/browser-kit": "~2.3", - "symfony/css-selector": "~2.3", - "symfony/dom-crawler": "~2.3", - "symfony/form": "~2.3" - }, - "time": "2015-06-04 21:24:58", + "time": "2018-01-12T07:16:03+00:00", "type": "library", "extra": { "branch-alias": { - "dev-master": "1.2.x-dev" + "dev-master": "2.2.x-dev" } }, "installation-source": "dist", "autoload": { - "psr-0": { - "Silex": "src/" + "psr-4": { + "Silex\\": "src/Silex" } }, "notification-url": "https://packagist.org/downloads/", @@ -165,45 +286,520 @@ "email": "igor@wiedler.ch" } ], - "description": "The PHP micro-framework based on the Symfony2 Components", + "description": "The PHP micro-framework based on the Symfony Components", "homepage": "http://silex.sensiolabs.org", "keywords": [ "microframework" ] }, { - "name": "twig/twig", - "version": "v1.21.2", - "version_normalized": "1.21.2.0", + "name": "symfony/debug", + "version": "v3.4.4", + "version_normalized": "3.4.4.0", "source": { "type": "git", - "url": "https://github.com/twigphp/Twig.git", - "reference": "ddce1136beb8db29b9cd7dffa8ab518b978c9db3" + "url": "https://github.com/symfony/debug.git", + "reference": "53f6af2805daf52a43b393b93d2f24925d35c937" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/twigphp/Twig/zipball/ddce1136beb8db29b9cd7dffa8ab518b978c9db3", - "reference": "ddce1136beb8db29b9cd7dffa8ab518b978c9db3", + "url": "https://api.github.com/repos/symfony/debug/zipball/53f6af2805daf52a43b393b93d2f24925d35c937", + "reference": "53f6af2805daf52a43b393b93d2f24925d35c937", "shasum": "" }, "require": { - "php": ">=5.2.7" + "php": "^5.5.9|>=7.0.8", + "psr/log": "~1.0" + }, + "conflict": { + "symfony/http-kernel": ">=2.3,<2.3.24|~2.4.0|>=2.5,<2.5.9|>=2.6,<2.6.2" }, "require-dev": { - "symfony/debug": "~2.7", - "symfony/phpunit-bridge": "~2.7" + "symfony/http-kernel": "~2.8|~3.0|~4.0" }, - "time": "2015-09-09 05:28:51", + "time": "2018-01-18T22:16:57+00:00", "type": "library", "extra": { "branch-alias": { - "dev-master": "1.21-dev" + "dev-master": "3.4-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Symfony\\Component\\Debug\\": "" + }, + "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": "Symfony Debug Component", + "homepage": "https://symfony.com" + }, + { + "name": "symfony/event-dispatcher", + "version": "v3.4.4", + "version_normalized": "3.4.4.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/event-dispatcher.git", + "reference": "26b87b6bca8f8f797331a30b76fdae5342dc26ca" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/26b87b6bca8f8f797331a30b76fdae5342dc26ca", + "reference": "26b87b6bca8f8f797331a30b76fdae5342dc26ca", + "shasum": "" + }, + "require": { + "php": "^5.5.9|>=7.0.8" + }, + "conflict": { + "symfony/dependency-injection": "<3.3" + }, + "require-dev": { + "psr/log": "~1.0", + "symfony/config": "~2.8|~3.0|~4.0", + "symfony/dependency-injection": "~3.3|~4.0", + "symfony/expression-language": "~2.8|~3.0|~4.0", + "symfony/stopwatch": "~2.8|~3.0|~4.0" + }, + "suggest": { + "symfony/dependency-injection": "", + "symfony/http-kernel": "" + }, + "time": "2018-01-03T07:37:34+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.4-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Symfony\\Component\\EventDispatcher\\": "" + }, + "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": "Symfony EventDispatcher Component", + "homepage": "https://symfony.com" + }, + { + "name": "symfony/http-foundation", + "version": "v3.4.4", + "version_normalized": "3.4.4.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/http-foundation.git", + "reference": "8c39071ac9cc7e6d8dab1d556c990dc0d2cc3d30" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/8c39071ac9cc7e6d8dab1d556c990dc0d2cc3d30", + "reference": "8c39071ac9cc7e6d8dab1d556c990dc0d2cc3d30", + "shasum": "" + }, + "require": { + "php": "^5.5.9|>=7.0.8", + "symfony/polyfill-mbstring": "~1.1", + "symfony/polyfill-php70": "~1.6" + }, + "require-dev": { + "symfony/expression-language": "~2.8|~3.0|~4.0" + }, + "time": "2018-01-29T09:03:43+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.4-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Symfony\\Component\\HttpFoundation\\": "" + }, + "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": "Symfony HttpFoundation Component", + "homepage": "https://symfony.com" + }, + { + "name": "symfony/http-kernel", + "version": "v3.4.4", + "version_normalized": "3.4.4.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/http-kernel.git", + "reference": "911d2e5dd4beb63caad9a72e43857de984301907" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/http-kernel/zipball/911d2e5dd4beb63caad9a72e43857de984301907", + "reference": "911d2e5dd4beb63caad9a72e43857de984301907", + "shasum": "" + }, + "require": { + "php": "^5.5.9|>=7.0.8", + "psr/log": "~1.0", + "symfony/debug": "~2.8|~3.0|~4.0", + "symfony/event-dispatcher": "~2.8|~3.0|~4.0", + "symfony/http-foundation": "^3.4.4|^4.0.4" + }, + "conflict": { + "symfony/config": "<2.8", + "symfony/dependency-injection": "<3.4", + "symfony/var-dumper": "<3.3", + "twig/twig": "<1.34|<2.4,>=2" + }, + "provide": { + "psr/log-implementation": "1.0" + }, + "require-dev": { + "psr/cache": "~1.0", + "symfony/browser-kit": "~2.8|~3.0|~4.0", + "symfony/class-loader": "~2.8|~3.0", + "symfony/config": "~2.8|~3.0|~4.0", + "symfony/console": "~2.8|~3.0|~4.0", + "symfony/css-selector": "~2.8|~3.0|~4.0", + "symfony/dependency-injection": "~3.4|~4.0", + "symfony/dom-crawler": "~2.8|~3.0|~4.0", + "symfony/expression-language": "~2.8|~3.0|~4.0", + "symfony/finder": "~2.8|~3.0|~4.0", + "symfony/process": "~2.8|~3.0|~4.0", + "symfony/routing": "~3.4|~4.0", + "symfony/stopwatch": "~2.8|~3.0|~4.0", + "symfony/templating": "~2.8|~3.0|~4.0", + "symfony/translation": "~2.8|~3.0|~4.0", + "symfony/var-dumper": "~3.3|~4.0" + }, + "suggest": { + "symfony/browser-kit": "", + "symfony/config": "", + "symfony/console": "", + "symfony/dependency-injection": "", + "symfony/finder": "", + "symfony/var-dumper": "" + }, + "time": "2018-01-29T12:29:46+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.4-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Symfony\\Component\\HttpKernel\\": "" + }, + "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": "Symfony HttpKernel Component", + "homepage": "https://symfony.com" + }, + { + "name": "symfony/polyfill-mbstring", + "version": "v1.6.0", + "version_normalized": "1.6.0.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "2ec8b39c38cb16674bbf3fea2b6ce5bf117e1296" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/2ec8b39c38cb16674bbf3fea2b6ce5bf117e1296", + "reference": "2ec8b39c38cb16674bbf3fea2b6ce5bf117e1296", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "suggest": { + "ext-mbstring": "For best performance" + }, + "time": "2017-10-11T12:05:26+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.6-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Mbstring\\": "" + }, + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for the Mbstring extension", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "mbstring", + "polyfill", + "portable", + "shim" + ] + }, + { + "name": "symfony/polyfill-php70", + "version": "v1.6.0", + "version_normalized": "1.6.0.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php70.git", + "reference": "0442b9c0596610bd24ae7b5f0a6cdbbc16d9fcff" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php70/zipball/0442b9c0596610bd24ae7b5f0a6cdbbc16d9fcff", + "reference": "0442b9c0596610bd24ae7b5f0a6cdbbc16d9fcff", + "shasum": "" + }, + "require": { + "paragonie/random_compat": "~1.0|~2.0", + "php": ">=5.3.3" + }, + "time": "2017-10-11T12:05:26+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.6-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Php70\\": "" + }, + "files": [ + "bootstrap.php" + ], + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 7.0+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ] + }, + { + "name": "symfony/routing", + "version": "v3.4.4", + "version_normalized": "3.4.4.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/routing.git", + "reference": "235d01730d553a97732990588407eaf6779bb4b2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/routing/zipball/235d01730d553a97732990588407eaf6779bb4b2", + "reference": "235d01730d553a97732990588407eaf6779bb4b2", + "shasum": "" + }, + "require": { + "php": "^5.5.9|>=7.0.8" + }, + "conflict": { + "symfony/config": "<2.8", + "symfony/dependency-injection": "<3.3", + "symfony/yaml": "<3.4" + }, + "require-dev": { + "doctrine/annotations": "~1.0", + "doctrine/common": "~2.2", + "psr/log": "~1.0", + "symfony/config": "~2.8|~3.0|~4.0", + "symfony/dependency-injection": "~3.3|~4.0", + "symfony/expression-language": "~2.8|~3.0|~4.0", + "symfony/http-foundation": "~2.8|~3.0|~4.0", + "symfony/yaml": "~3.4|~4.0" + }, + "suggest": { + "doctrine/annotations": "For using the annotation loader", + "symfony/config": "For using the all-in-one router or any loader", + "symfony/dependency-injection": "For loading routes from a service", + "symfony/expression-language": "For using expression matching", + "symfony/http-foundation": "For using a Symfony Request object", + "symfony/yaml": "For using the YAML loader" + }, + "time": "2018-01-16T18:03:57+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.4-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Symfony\\Component\\Routing\\": "" + }, + "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": "Symfony Routing Component", + "homepage": "https://symfony.com", + "keywords": [ + "router", + "routing", + "uri", + "url" + ] + }, + { + "name": "twig/twig", + "version": "v1.35.0", + "version_normalized": "1.35.0.0", + "source": { + "type": "git", + "url": "https://github.com/twigphp/Twig.git", + "reference": "daa657073e55b0a78cce8fdd22682fddecc6385f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/twigphp/Twig/zipball/daa657073e55b0a78cce8fdd22682fddecc6385f", + "reference": "daa657073e55b0a78cce8fdd22682fddecc6385f", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "psr/container": "^1.0", + "symfony/debug": "~2.7", + "symfony/phpunit-bridge": "~3.3@dev" + }, + "time": "2017-09-27T18:06:46+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.35-dev" } }, "installation-source": "dist", "autoload": { "psr-0": { "Twig_": "lib/" + }, + "psr-4": { + "Twig\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -233,336 +829,5 @@ "keywords": [ "templating" ] - }, - { - "name": "symfony/debug", - "version": "v2.6.12", - "version_normalized": "2.6.12.0", - "target-dir": "Symfony/Component/Debug", - "source": { - "type": "git", - "url": "https://github.com/symfony/debug.git", - "reference": "fca5696e0c9787722baa8f2ad6940dfd7a6a6941" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/debug/zipball/fca5696e0c9787722baa8f2ad6940dfd7a6a6941", - "reference": "fca5696e0c9787722baa8f2ad6940dfd7a6a6941", - "shasum": "" - }, - "require": { - "php": ">=5.3.3", - "psr/log": "~1.0" - }, - "conflict": { - "symfony/http-kernel": ">=2.3,<2.3.24|~2.4.0|>=2.5,<2.5.9|>=2.6,<2.6.2" - }, - "require-dev": { - "symfony/class-loader": "~2.2", - "symfony/http-foundation": "~2.1", - "symfony/http-kernel": "~2.3.24|~2.5.9|~2.6,>=2.6.2", - "symfony/phpunit-bridge": "~2.7" - }, - "suggest": { - "symfony/http-foundation": "", - "symfony/http-kernel": "" - }, - "time": "2015-07-08 05:59:48", - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.6-dev" - } - }, - "installation-source": "dist", - "autoload": { - "psr-0": { - "Symfony\\Component\\Debug\\": "" - } - }, - "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": "Symfony Debug Component", - "homepage": "https://symfony.com" - }, - { - "name": "symfony/http-foundation", - "version": "v2.6.12", - "version_normalized": "2.6.12.0", - "target-dir": "Symfony/Component/HttpFoundation", - "source": { - "type": "git", - "url": "https://github.com/symfony/http-foundation.git", - "reference": "e8fd1b73ac1c3de1f76c73801ddf1a8ecb1c1c9c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/http-foundation/zipball/e8fd1b73ac1c3de1f76c73801ddf1a8ecb1c1c9c", - "reference": "e8fd1b73ac1c3de1f76c73801ddf1a8ecb1c1c9c", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "require-dev": { - "symfony/expression-language": "~2.4", - "symfony/phpunit-bridge": "~2.7" - }, - "time": "2015-07-22 10:08:40", - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.6-dev" - } - }, - "installation-source": "dist", - "autoload": { - "psr-0": { - "Symfony\\Component\\HttpFoundation\\": "" - }, - "classmap": [ - "Symfony/Component/HttpFoundation/Resources/stubs" - ] - }, - "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": "Symfony HttpFoundation Component", - "homepage": "https://symfony.com" - }, - { - "name": "symfony/event-dispatcher", - "version": "v2.6.12", - "version_normalized": "2.6.12.0", - "target-dir": "Symfony/Component/EventDispatcher", - "source": { - "type": "git", - "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "672593bc4b0043a0acf91903bb75a1c82d8f2e02" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/672593bc4b0043a0acf91903bb75a1c82d8f2e02", - "reference": "672593bc4b0043a0acf91903bb75a1c82d8f2e02", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "require-dev": { - "psr/log": "~1.0", - "symfony/config": "~2.0,>=2.0.5", - "symfony/dependency-injection": "~2.6", - "symfony/expression-language": "~2.6", - "symfony/phpunit-bridge": "~2.7", - "symfony/stopwatch": "~2.3" - }, - "suggest": { - "symfony/dependency-injection": "", - "symfony/http-kernel": "" - }, - "time": "2015-05-02 15:18:45", - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.6-dev" - } - }, - "installation-source": "dist", - "autoload": { - "psr-0": { - "Symfony\\Component\\EventDispatcher\\": "" - } - }, - "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": "Symfony EventDispatcher Component", - "homepage": "https://symfony.com" - }, - { - "name": "symfony/http-kernel", - "version": "v2.6.12", - "version_normalized": "2.6.12.0", - "target-dir": "Symfony/Component/HttpKernel", - "source": { - "type": "git", - "url": "https://github.com/symfony/http-kernel.git", - "reference": "498866a8ca0bcbcd3f3824b1520fa568ff7ca3b6" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/http-kernel/zipball/498866a8ca0bcbcd3f3824b1520fa568ff7ca3b6", - "reference": "498866a8ca0bcbcd3f3824b1520fa568ff7ca3b6", - "shasum": "" - }, - "require": { - "php": ">=5.3.3", - "psr/log": "~1.0", - "symfony/debug": "~2.6,>=2.6.2", - "symfony/event-dispatcher": "~2.6,>=2.6.7", - "symfony/http-foundation": "~2.5,>=2.5.4" - }, - "require-dev": { - "symfony/browser-kit": "~2.3", - "symfony/class-loader": "~2.1", - "symfony/config": "~2.0,>=2.0.5", - "symfony/console": "~2.3", - "symfony/css-selector": "~2.0,>=2.0.5", - "symfony/dependency-injection": "~2.2", - "symfony/dom-crawler": "~2.0,>=2.0.5", - "symfony/expression-language": "~2.4", - "symfony/finder": "~2.0,>=2.0.5", - "symfony/phpunit-bridge": "~2.7", - "symfony/process": "~2.0,>=2.0.5", - "symfony/routing": "~2.2", - "symfony/stopwatch": "~2.3", - "symfony/templating": "~2.2", - "symfony/translation": "~2.0,>=2.0.5", - "symfony/var-dumper": "~2.6" - }, - "suggest": { - "symfony/browser-kit": "", - "symfony/class-loader": "", - "symfony/config": "", - "symfony/console": "", - "symfony/dependency-injection": "", - "symfony/finder": "", - "symfony/var-dumper": "" - }, - "time": "2015-11-23 11:37:53", - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.6-dev" - } - }, - "installation-source": "dist", - "autoload": { - "psr-0": { - "Symfony\\Component\\HttpKernel\\": "" - } - }, - "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": "Symfony HttpKernel Component", - "homepage": "https://symfony.com" - }, - { - "name": "symfony/routing", - "version": "v2.6.12", - "version_normalized": "2.6.12.0", - "target-dir": "Symfony/Component/Routing", - "source": { - "type": "git", - "url": "https://github.com/symfony/routing.git", - "reference": "0a1764d41bbb54f3864808c50569ac382b44d128" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/routing/zipball/0a1764d41bbb54f3864808c50569ac382b44d128", - "reference": "0a1764d41bbb54f3864808c50569ac382b44d128", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "require-dev": { - "doctrine/annotations": "~1.0", - "doctrine/common": "~2.2", - "psr/log": "~1.0", - "symfony/config": "~2.2", - "symfony/expression-language": "~2.4", - "symfony/http-foundation": "~2.3", - "symfony/phpunit-bridge": "~2.7", - "symfony/yaml": "~2.0,>=2.0.5" - }, - "suggest": { - "doctrine/annotations": "For using the annotation loader", - "symfony/config": "For using the all-in-one router or any loader", - "symfony/expression-language": "For using expression matching", - "symfony/yaml": "For using the YAML loader" - }, - "time": "2015-07-09 16:02:48", - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.6-dev" - } - }, - "installation-source": "dist", - "autoload": { - "psr-0": { - "Symfony\\Component\\Routing\\": "" - } - }, - "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": "Symfony Routing Component", - "homepage": "https://symfony.com", - "keywords": [ - "router", - "routing", - "uri", - "url" - ] } ] diff --git a/lib/silex/vendor/paragonie/random_compat/LICENSE b/lib/silex/vendor/paragonie/random_compat/LICENSE new file mode 100644 index 000000000..45c7017df --- /dev/null +++ b/lib/silex/vendor/paragonie/random_compat/LICENSE @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) 2015 Paragon Initiative Enterprises + +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/lib/silex/vendor/paragonie/random_compat/build-phar.sh b/lib/silex/vendor/paragonie/random_compat/build-phar.sh new file mode 100644 index 000000000..b4a5ba31c --- /dev/null +++ b/lib/silex/vendor/paragonie/random_compat/build-phar.sh @@ -0,0 +1,5 @@ +#!/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/lib/silex/vendor/paragonie/random_compat/composer.json b/lib/silex/vendor/paragonie/random_compat/composer.json new file mode 100644 index 000000000..1c5978c6f --- /dev/null +++ b/lib/silex/vendor/paragonie/random_compat/composer.json @@ -0,0 +1,37 @@ +{ + "name": "paragonie/random_compat", + "description": "PHP 5.x polyfill for random_bytes() and random_int() from PHP 7", + "keywords": [ + "csprng", + "random", + "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": ">=5.2.0" + }, + "require-dev": { + "phpunit/phpunit": "4.*|5.*" + }, + "suggest": { + "ext-libsodium": "Provides a modern crypto API that can be used to generate random bytes." + }, + "autoload": { + "files": [ + "lib/random.php" + ] + } +} diff --git a/lib/silex/vendor/paragonie/random_compat/dist/random_compat.phar.pubkey b/lib/silex/vendor/paragonie/random_compat/dist/random_compat.phar.pubkey new file mode 100644 index 000000000..eb50ebfcd --- /dev/null +++ b/lib/silex/vendor/paragonie/random_compat/dist/random_compat.phar.pubkey @@ -0,0 +1,5 @@ +-----BEGIN PUBLIC KEY----- +MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEEd+wCqJDrx5B4OldM0dQE0ZMX+lx1ZWm +pui0SUqD4G29L3NGsz9UhJ/0HjBdbnkhIK5xviT0X5vtjacF6ajgcCArbTB+ds+p ++h7Q084NuSuIpNb6YPfoUFgC/CL9kAoc +-----END PUBLIC KEY----- diff --git a/lib/silex/vendor/paragonie/random_compat/dist/random_compat.phar.pubkey.asc b/lib/silex/vendor/paragonie/random_compat/dist/random_compat.phar.pubkey.asc new file mode 100644 index 000000000..6a1d7f300 --- /dev/null +++ b/lib/silex/vendor/paragonie/random_compat/dist/random_compat.phar.pubkey.asc @@ -0,0 +1,11 @@ +-----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/lib/silex/vendor/paragonie/random_compat/lib/byte_safe_strings.php b/lib/silex/vendor/paragonie/random_compat/lib/byte_safe_strings.php new file mode 100644 index 000000000..3de86b223 --- /dev/null +++ b/lib/silex/vendor/paragonie/random_compat/lib/byte_safe_strings.php @@ -0,0 +1,181 @@ + RandomCompat_strlen($binary_string)) { + return ''; + } + + return (string) mb_substr($binary_string, $start, $length, '8bit'); + } + + } else { + + /** + * substr() implementation that isn't brittle to mbstring.func_overload + * + * This version just uses the default substr() + * + * @param string $binary_string + * @param int $start + * @param int $length (optional) + * + * @throws TypeError + * + * @return string + */ + function RandomCompat_substr($binary_string, $start, $length = null) + { + if (!is_string($binary_string)) { + throw new TypeError( + 'RandomCompat_substr(): First argument should be a string' + ); + } + + if (!is_int($start)) { + throw new TypeError( + 'RandomCompat_substr(): Second argument should be an integer' + ); + } + + if ($length !== null) { + if (!is_int($length)) { + throw new TypeError( + 'RandomCompat_substr(): Third argument should be an integer, or omitted' + ); + } + + return (string) substr($binary_string, $start, $length); + } + + return (string) substr($binary_string, $start); + } + } +} diff --git a/lib/silex/vendor/paragonie/random_compat/lib/cast_to_int.php b/lib/silex/vendor/paragonie/random_compat/lib/cast_to_int.php new file mode 100644 index 000000000..9a4fab991 --- /dev/null +++ b/lib/silex/vendor/paragonie/random_compat/lib/cast_to_int.php @@ -0,0 +1,75 @@ + operators might accidentally let a float + * through. + * + * @param int|float $number The number we want to convert to an int + * @param bool $fail_open Set to true to not throw an exception + * + * @return float|int + * @psalm-suppress InvalidReturnType + * + * @throws TypeError + */ + function RandomCompat_intval($number, $fail_open = false) + { + if (is_int($number) || is_float($number)) { + $number += 0; + } elseif (is_numeric($number)) { + $number += 0; + } + + if ( + is_float($number) + && + $number > ~PHP_INT_MAX + && + $number < PHP_INT_MAX + ) { + $number = (int) $number; + } + + if (is_int($number)) { + return (int) $number; + } elseif (!$fail_open) { + throw new TypeError( + 'Expected an integer.' + ); + } + return $number; + } +} diff --git a/lib/silex/vendor/paragonie/random_compat/lib/error_polyfill.php b/lib/silex/vendor/paragonie/random_compat/lib/error_polyfill.php new file mode 100644 index 000000000..6a91990ce --- /dev/null +++ b/lib/silex/vendor/paragonie/random_compat/lib/error_polyfill.php @@ -0,0 +1,49 @@ += 70000) { + return; +} + +if (!defined('RANDOM_COMPAT_READ_BUFFER')) { + define('RANDOM_COMPAT_READ_BUFFER', 8); +} + +$RandomCompatDIR = dirname(__FILE__); + +require_once $RandomCompatDIR . '/byte_safe_strings.php'; +require_once $RandomCompatDIR . '/cast_to_int.php'; +require_once $RandomCompatDIR . '/error_polyfill.php'; + +if (!is_callable('random_bytes')) { + /** + * PHP 5.2.0 - 5.6.x way to implement random_bytes() + * + * We use conditional statements here to define the function in accordance + * to the operating environment. It's a micro-optimization. + * + * In order of preference: + * 1. Use libsodium if available. + * 2. fread() /dev/urandom if available (never on Windows) + * 3. mcrypt_create_iv($bytes, MCRYPT_DEV_URANDOM) + * 4. COM('CAPICOM.Utilities.1')->GetRandom() + * + * See RATIONALE.md for our reasoning behind this particular order + */ + if (extension_loaded('libsodium')) { + // See random_bytes_libsodium.php + if (PHP_VERSION_ID >= 50300 && is_callable('\\Sodium\\randombytes_buf')) { + require_once $RandomCompatDIR . '/random_bytes_libsodium.php'; + } elseif (method_exists('Sodium', 'randombytes_buf')) { + require_once $RandomCompatDIR . '/random_bytes_libsodium_legacy.php'; + } + } + + /** + * Reading directly from /dev/urandom: + */ + if (DIRECTORY_SEPARATOR === '/') { + // DIRECTORY_SEPARATOR === '/' on Unix-like OSes -- this is a fast + // way to exclude Windows. + $RandomCompatUrandom = true; + $RandomCompat_basedir = ini_get('open_basedir'); + + if (!empty($RandomCompat_basedir)) { + $RandomCompat_open_basedir = explode( + PATH_SEPARATOR, + strtolower($RandomCompat_basedir) + ); + $RandomCompatUrandom = (array() !== array_intersect( + array('/dev', '/dev/', '/dev/urandom'), + $RandomCompat_open_basedir + )); + $RandomCompat_open_basedir = null; + } + + if ( + !is_callable('random_bytes') + && + $RandomCompatUrandom + && + @is_readable('/dev/urandom') + ) { + // Error suppression on is_readable() in case of an open_basedir + // or safe_mode failure. All we care about is whether or not we + // can read it at this point. If the PHP environment is going to + // panic over trying to see if the file can be read in the first + // place, that is not helpful to us here. + + // See random_bytes_dev_urandom.php + require_once $RandomCompatDIR . '/random_bytes_dev_urandom.php'; + } + // Unset variables after use + $RandomCompat_basedir = null; + } else { + $RandomCompatUrandom = false; + } + + /** + * mcrypt_create_iv() + * + * We only want to use mcypt_create_iv() if: + * + * - random_bytes() hasn't already been defined + * - the mcrypt extensions is loaded + * - One of these two conditions is true: + * - We're on Windows (DIRECTORY_SEPARATOR !== '/') + * - We're not on Windows and /dev/urandom is readabale + * (i.e. we're not in a chroot jail) + * - Special case: + * - If we're not on Windows, but the PHP version is between + * 5.6.10 and 5.6.12, we don't want to use mcrypt. It will + * hang indefinitely. This is bad. + * - If we're on Windows, we want to use PHP >= 5.3.7 or else + * we get insufficient entropy errors. + */ + if ( + !is_callable('random_bytes') + && + // Windows on PHP < 5.3.7 is broken, but non-Windows is not known to be. + (DIRECTORY_SEPARATOR === '/' || PHP_VERSION_ID >= 50307) + && + // Prevent this code from hanging indefinitely on non-Windows; + // see https://bugs.php.net/bug.php?id=69833 + ( + DIRECTORY_SEPARATOR !== '/' || + (PHP_VERSION_ID <= 50609 || PHP_VERSION_ID >= 50613) + ) + && + extension_loaded('mcrypt') + ) { + // See random_bytes_mcrypt.php + require_once $RandomCompatDIR . '/random_bytes_mcrypt.php'; + } + $RandomCompatUrandom = null; + + /** + * This is a Windows-specific fallback, for when the mcrypt extension + * isn't loaded. + */ + if ( + !is_callable('random_bytes') + && + extension_loaded('com_dotnet') + && + class_exists('COM') + ) { + $RandomCompat_disabled_classes = preg_split( + '#\s*,\s*#', + strtolower(ini_get('disable_classes')) + ); + + if (!in_array('com', $RandomCompat_disabled_classes)) { + try { + $RandomCompatCOMtest = new COM('CAPICOM.Utilities.1'); + if (method_exists($RandomCompatCOMtest, 'GetRandom')) { + // See random_bytes_com_dotnet.php + require_once $RandomCompatDIR . '/random_bytes_com_dotnet.php'; + } + } catch (com_exception $e) { + // Don't try to use it. + } + } + $RandomCompat_disabled_classes = null; + $RandomCompatCOMtest = null; + } + + /** + * throw new Exception + */ + if (!is_callable('random_bytes')) { + /** + * We don't have any more options, so let's throw an exception right now + * and hope the developer won't let it fail silently. + * + * @param mixed $length + * @return void + * @throws Exception + */ + function random_bytes($length) + { + unset($length); // Suppress "variable not used" warnings. + throw new Exception( + 'There is no suitable CSPRNG installed on your system' + ); + } + } +} + +if (!is_callable('random_int')) { + require_once $RandomCompatDIR . '/random_int.php'; +} + +$RandomCompatDIR = null; diff --git a/lib/silex/vendor/paragonie/random_compat/lib/random_bytes_com_dotnet.php b/lib/silex/vendor/paragonie/random_compat/lib/random_bytes_com_dotnet.php new file mode 100644 index 000000000..fc1926e5c --- /dev/null +++ b/lib/silex/vendor/paragonie/random_compat/lib/random_bytes_com_dotnet.php @@ -0,0 +1,88 @@ +GetRandom($bytes, 0)); + if (RandomCompat_strlen($buf) >= $bytes) { + /** + * Return our random entropy buffer here: + */ + return RandomCompat_substr($buf, 0, $bytes); + } + ++$execCount; + } while ($execCount < $bytes); + + /** + * If we reach here, PHP has failed us. + */ + throw new Exception( + 'Could not gather sufficient random data' + ); + } +} \ No newline at end of file diff --git a/lib/silex/vendor/paragonie/random_compat/lib/random_bytes_dev_urandom.php b/lib/silex/vendor/paragonie/random_compat/lib/random_bytes_dev_urandom.php new file mode 100644 index 000000000..df5b91524 --- /dev/null +++ b/lib/silex/vendor/paragonie/random_compat/lib/random_bytes_dev_urandom.php @@ -0,0 +1,167 @@ + 0); + + /** + * Is our result valid? + */ + if (is_string($buf)) { + if (RandomCompat_strlen($buf) === $bytes) { + /** + * Return our random entropy buffer here: + */ + return $buf; + } + } + } + + /** + * If we reach here, PHP has failed us. + */ + throw new Exception( + 'Error reading from source device' + ); + } +} diff --git a/lib/silex/vendor/paragonie/random_compat/lib/random_bytes_libsodium.php b/lib/silex/vendor/paragonie/random_compat/lib/random_bytes_libsodium.php new file mode 100644 index 000000000..4af1a2422 --- /dev/null +++ b/lib/silex/vendor/paragonie/random_compat/lib/random_bytes_libsodium.php @@ -0,0 +1,88 @@ + 2147483647) { + $buf = ''; + for ($i = 0; $i < $bytes; $i += 1073741824) { + $n = ($bytes - $i) > 1073741824 + ? 1073741824 + : $bytes - $i; + $buf .= \Sodium\randombytes_buf($n); + } + } else { + $buf = \Sodium\randombytes_buf($bytes); + } + + if ($buf !== false) { + if (RandomCompat_strlen($buf) === $bytes) { + return $buf; + } + } + + /** + * If we reach here, PHP has failed us. + */ + throw new Exception( + 'Could not gather sufficient random data' + ); + } +} diff --git a/lib/silex/vendor/paragonie/random_compat/lib/random_bytes_libsodium_legacy.php b/lib/silex/vendor/paragonie/random_compat/lib/random_bytes_libsodium_legacy.php new file mode 100644 index 000000000..705af5262 --- /dev/null +++ b/lib/silex/vendor/paragonie/random_compat/lib/random_bytes_libsodium_legacy.php @@ -0,0 +1,92 @@ + 2147483647) { + for ($i = 0; $i < $bytes; $i += 1073741824) { + $n = ($bytes - $i) > 1073741824 + ? 1073741824 + : $bytes - $i; + $buf .= Sodium::randombytes_buf((int) $n); + } + } else { + $buf .= Sodium::randombytes_buf((int) $bytes); + } + + if (is_string($buf)) { + if (RandomCompat_strlen($buf) === $bytes) { + return $buf; + } + } + + /** + * If we reach here, PHP has failed us. + */ + throw new Exception( + 'Could not gather sufficient random data' + ); + } +} diff --git a/lib/silex/vendor/paragonie/random_compat/lib/random_bytes_mcrypt.php b/lib/silex/vendor/paragonie/random_compat/lib/random_bytes_mcrypt.php new file mode 100644 index 000000000..aac9c013d --- /dev/null +++ b/lib/silex/vendor/paragonie/random_compat/lib/random_bytes_mcrypt.php @@ -0,0 +1,77 @@ + operators might accidentally let a float + * through. + */ + + try { + $min = RandomCompat_intval($min); + } catch (TypeError $ex) { + throw new TypeError( + 'random_int(): $min must be an integer' + ); + } + + try { + $max = RandomCompat_intval($max); + } catch (TypeError $ex) { + throw new TypeError( + 'random_int(): $max must be an integer' + ); + } + + /** + * Now that we've verified our weak typing system has given us an integer, + * let's validate the logic then we can move forward with generating random + * integers along a given range. + */ + if ($min > $max) { + throw new Error( + 'Minimum value must be less than or equal to the maximum value' + ); + } + + if ($max === $min) { + return (int) $min; + } + + /** + * Initialize variables to 0 + * + * We want to store: + * $bytes => the number of random bytes we need + * $mask => an integer bitmask (for use with the &) operator + * so we can minimize the number of discards + */ + $attempts = $bits = $bytes = $mask = $valueShift = 0; + + /** + * At this point, $range is a positive number greater than 0. It might + * overflow, however, if $max - $min > PHP_INT_MAX. PHP will cast it to + * a float and we will lose some precision. + */ + $range = $max - $min; + + /** + * Test for integer overflow: + */ + if (!is_int($range)) { + + /** + * Still safely calculate wider ranges. + * Provided by @CodesInChaos, @oittaa + * + * @ref https://gist.github.com/CodesInChaos/03f9ea0b58e8b2b8d435 + * + * We use ~0 as a mask in this case because it generates all 1s + * + * @ref https://eval.in/400356 (32-bit) + * @ref http://3v4l.org/XX9r5 (64-bit) + */ + $bytes = PHP_INT_SIZE; + $mask = ~0; + + } else { + + /** + * $bits is effectively ceil(log($range, 2)) without dealing with + * type juggling + */ + while ($range > 0) { + if ($bits % 8 === 0) { + ++$bytes; + } + ++$bits; + $range >>= 1; + $mask = $mask << 1 | 1; + } + $valueShift = $min; + } + + $val = 0; + /** + * Now that we have our parameters set up, let's begin generating + * random integers until one falls between $min and $max + */ + do { + /** + * The rejection probability is at most 0.5, so this corresponds + * to a failure probability of 2^-128 for a working RNG + */ + if ($attempts > 128) { + throw new Exception( + 'random_int: RNG is broken - too many rejections' + ); + } + + /** + * Let's grab the necessary number of random bytes + */ + $randomByteString = random_bytes($bytes); + + /** + * Let's turn $randomByteString into an integer + * + * This uses bitwise operators (<< and |) to build an integer + * out of the values extracted from ord() + * + * Example: [9F] | [6D] | [32] | [0C] => + * 159 + 27904 + 3276800 + 201326592 => + * 204631455 + */ + $val &= 0; + for ($i = 0; $i < $bytes; ++$i) { + $val |= ord($randomByteString[$i]) << ($i * 8); + } + + /** + * Apply mask + */ + $val &= $mask; + $val += $valueShift; + + ++$attempts; + /** + * If $val overflows to a floating point number, + * ... or is larger than $max, + * ... or smaller than $min, + * then try again. + */ + } while (!is_int($val) || $val > $max || $val < $min); + + return (int) $val; + } +} diff --git a/lib/silex/vendor/paragonie/random_compat/other/build_phar.php b/lib/silex/vendor/paragonie/random_compat/other/build_phar.php new file mode 100644 index 000000000..70ef4b2ed --- /dev/null +++ b/lib/silex/vendor/paragonie/random_compat/other/build_phar.php @@ -0,0 +1,57 @@ +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/lib/silex/vendor/paragonie/random_compat/psalm-autoload.php b/lib/silex/vendor/paragonie/random_compat/psalm-autoload.php new file mode 100644 index 000000000..d71d1b818 --- /dev/null +++ b/lib/silex/vendor/paragonie/random_compat/psalm-autoload.php @@ -0,0 +1,9 @@ + + + + + + + + + + + + diff --git a/lib/silex/vendor/pimple/pimple/.gitignore b/lib/silex/vendor/pimple/pimple/.gitignore index ce3aa6521..c089b0952 100644 --- a/lib/silex/vendor/pimple/pimple/.gitignore +++ b/lib/silex/vendor/pimple/pimple/.gitignore @@ -1 +1,3 @@ phpunit.xml +composer.lock +/vendor/ diff --git a/lib/silex/vendor/pimple/pimple/.travis.yml b/lib/silex/vendor/pimple/pimple/.travis.yml new file mode 100644 index 000000000..196f7fc19 --- /dev/null +++ b/lib/silex/vendor/pimple/pimple/.travis.yml @@ -0,0 +1,40 @@ +language: php + +env: + matrix: + - PIMPLE_EXT=no + - PIMPLE_EXT=yes + global: + - REPORT_EXIT_STATUS=1 + +php: + - 5.3 + - 5.4 + - 5.5 + - 5.6 + - 7.0 + - 7.1 + +before_script: + - composer self-update + - COMPOSER_ROOT_VERSION=dev-master composer install + - if [ "$PIMPLE_EXT" == "yes" ]; then sh -c "cd ext/pimple && phpize && ./configure && make && sudo make install"; fi + - if [ "$PIMPLE_EXT" == "yes" ]; then echo "extension=pimple.so" >> `php --ini | grep "Loaded Configuration" | sed -e "s|.*:\s*||"`; fi + +script: + - cd ext/pimple + - if [ "$PIMPLE_EXT" == "yes" ]; then yes n | make test | tee output ; grep -E 'Tests failed +. +0' output; fi + - if [ "$PIMPLE_EXT" == "yes" ]; then export SYMFONY_DEPRECATIONS_HELPER=weak; fi + - cd ../.. + - ./vendor/bin/simple-phpunit + +matrix: + include: + - php: hhvm + dist: trusty + env: PIMPLE_EXT=no + exclude: + - php: 7.0 + env: PIMPLE_EXT=yes + - php: 7.1 + env: PIMPLE_EXT=yes diff --git a/lib/silex/vendor/pimple/pimple/CHANGELOG b/lib/silex/vendor/pimple/pimple/CHANGELOG new file mode 100644 index 000000000..ba56760c1 --- /dev/null +++ b/lib/silex/vendor/pimple/pimple/CHANGELOG @@ -0,0 +1,59 @@ +* 3.2.3 (2017-XX-XX) + + * n/a + +* 3.2.2 (2017-07-23) + + * reverted extending a protected closure throws an exception (deprecated it instead) + +* 3.2.1 (2017-07-17) + + * fixed PHP error + +* 3.2.0 (2017-07-17) + + * added a PSR-11 service locator + * added a PSR-11 wrapper + * added ServiceIterator + * fixed extending a protected closure (now throws InvalidServiceIdentifierException) + +* 3.1.0 (2017-07-03) + + * deprecated the C extension + * added support for PSR-11 exceptions + +* 3.0.2 (2015-09-11) + + * refactored the C extension + * minor non-significant changes + +* 3.0.1 (2015-07-30) + + * simplified some code + * fixed a segfault in the C extension + +* 3.0.0 (2014-07-24) + + * removed the Pimple class alias (use Pimple\Container instead) + +* 2.1.1 (2014-07-24) + + * fixed compiler warnings for the C extension + * fixed code when dealing with circular references + +* 2.1.0 (2014-06-24) + + * moved the Pimple to Pimple\Container (with a BC layer -- Pimple is now a + deprecated alias which will be removed in Pimple 3.0) + * added Pimple\ServiceProviderInterface (and Pimple::register()) + +* 2.0.0 (2014-02-10) + + * changed extend to automatically re-assign the extended service and keep it as shared or factory + (to keep BC, extend still returns the extended service) + * changed services to be shared by default (use factory() for factory + services) + +* 1.0.0 + + * initial version diff --git a/lib/silex/vendor/pimple/pimple/LICENSE b/lib/silex/vendor/pimple/pimple/LICENSE index f61e90d96..e02dc5a78 100644 --- a/lib/silex/vendor/pimple/pimple/LICENSE +++ b/lib/silex/vendor/pimple/pimple/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2009-2013 Fabien Potencier +Copyright (c) 2009-2017 Fabien Potencier 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/lib/silex/vendor/pimple/pimple/README.rst b/lib/silex/vendor/pimple/pimple/README.rst index f685caa62..a03b6d3a4 100644 --- a/lib/silex/vendor/pimple/pimple/README.rst +++ b/lib/silex/vendor/pimple/pimple/README.rst @@ -1,42 +1,53 @@ Pimple ====== -Pimple is a small Dependency Injection Container for PHP 5.3 that consists -of just one file and one class (about 80 lines of code). +.. caution:: -`Download it`_, require it in your code, and you're good to go:: + This is the documentation for Pimple 3.x. If you are using Pimple 1.x, read + the `Pimple 1.x documentation`_. Reading the Pimple 1.x code is also a good + way to learn more about how to create a simple Dependency Injection + Container (recent versions of Pimple are more focused on performance). - require_once '/path/to/Pimple.php'; +Pimple is a small Dependency Injection Container for PHP. -Creating a container is a matter of instating the ``Pimple`` class:: +Installation +------------ - $container = new Pimple(); +Before using Pimple in your project, add it to your ``composer.json`` file: -As many other dependency injection containers, Pimple is able to manage two -different kind of data: *services* and *parameters*. +.. code-block:: bash -Defining Parameters -------------------- + $ ./composer.phar require pimple/pimple "^3.0" -Defining a parameter is as simple as using the Pimple instance as an array:: +Usage +----- - // define some parameters - $container['cookie_name'] = 'SESSION_ID'; - $container['session_storage_class'] = 'SessionStorage'; +Creating a container is a matter of creating a ``Container`` instance: + +.. code-block:: php + + use Pimple\Container; + + $container = new Container(); + +As many other dependency injection containers, Pimple manages two different +kind of data: **services** and **parameters**. Defining Services ------------------ +~~~~~~~~~~~~~~~~~ -A service is an object that does something as part of a larger system. -Examples of services: Database connection, templating engine, mailer. Almost -any object could be a service. +A service is an object that does something as part of a larger system. Examples +of services: a database connection, a templating engine, or a mailer. Almost +any **global** object can be a service. -Services are defined by anonymous functions that return an instance of an -object:: +Services are defined by **anonymous functions** that return an instance of an +object: + +.. code-block:: php // define some services $container['session_storage'] = function ($c) { - return new $c['session_storage_class']($c['cookie_name']); + return new SessionStorage('SESSION_ID'); }; $container['session'] = function ($c) { @@ -47,9 +58,11 @@ Notice that the anonymous function has access to the current container instance, allowing references to other services or parameters. As objects are only created when you get them, the order of the definitions -does not matter, and there is no performance penalty. +does not matter. -Using the defined services is also very easy:: +Using the defined services is also very easy: + +.. code-block:: php // get the session object $session = $container['session']; @@ -58,102 +71,256 @@ Using the defined services is also very easy:: // $storage = new SessionStorage('SESSION_ID'); // $session = new Session($storage); -Defining Shared Services ------------------------- +Defining Factory Services +~~~~~~~~~~~~~~~~~~~~~~~~~ -By default, each time you get a service, Pimple returns a new instance of it. -If you want the same instance to be returned for all calls, wrap your -anonymous function with the ``share()`` method:: +By default, each time you get a service, Pimple returns the **same instance** +of it. If you want a different instance to be returned for all calls, wrap your +anonymous function with the ``factory()`` method - $container['session'] = $container->share(function ($c) { +.. code-block:: php + + $container['session'] = $container->factory(function ($c) { return new Session($c['session_storage']); }); +Now, each call to ``$container['session']`` returns a new instance of the +session. + +Defining Parameters +~~~~~~~~~~~~~~~~~~~ + +Defining a parameter allows to ease the configuration of your container from +the outside and to store global values: + +.. code-block:: php + + // define some parameters + $container['cookie_name'] = 'SESSION_ID'; + $container['session_storage_class'] = 'SessionStorage'; + +If you change the ``session_storage`` service definition like below: + +.. code-block:: php + + $container['session_storage'] = function ($c) { + return new $c['session_storage_class']($c['cookie_name']); + }; + +You can now easily change the cookie name by overriding the +``cookie_name`` parameter instead of redefining the service +definition. + Protecting Parameters ---------------------- +~~~~~~~~~~~~~~~~~~~~~ Because Pimple sees anonymous functions as service definitions, you need to wrap anonymous functions with the ``protect()`` method to store them as -parameter:: +parameters: - $container['random'] = $container->protect(function () { return rand(); }); +.. code-block:: php -Modifying services after creation ---------------------------------- + $container['random_func'] = $container->protect(function () { + return rand(); + }); + +Modifying Services after Definition +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ In some cases you may want to modify a service definition after it has been -defined. You can use the ``extend()`` method to define additional code to -be run on your service just after it is created:: +defined. You can use the ``extend()`` method to define additional code to be +run on your service just after it is created: - $container['mail'] = function ($c) { - return new \Zend_Mail(); +.. code-block:: php + + $container['session_storage'] = function ($c) { + return new $c['session_storage_class']($c['cookie_name']); }; - $container['mail'] = $container->extend('mail', function($mail, $c) { - $mail->setFrom($c['mail.default_from']); - return $mail; + $container->extend('session_storage', function ($storage, $c) { + $storage->...(); + + return $storage; }); -The first argument is the name of the object, the second is a function that -gets access to the object instance and the container. The return value is -a service definition, so you need to re-assign it on the container. +The first argument is the name of the service to extend, the second a function +that gets access to the object instance and the container. -If the service you plan to extend is already shared, it's recommended that you -re-wrap your extended service with the ``shared`` method, otherwise your extension -code will be called every time you access the service:: +Extending a Container +~~~~~~~~~~~~~~~~~~~~~ - $container['twig'] = $container->share(function ($c) { - return new Twig_Environment($c['twig.loader'], $c['twig.options']); - }); +If you use the same libraries over and over, you might want to reuse some +services from one project to the next one; package your services into a +**provider** by implementing ``Pimple\ServiceProviderInterface``: - $container['twig'] = $container->share($container->extend('twig', function ($twig, $c) { - $twig->addExtension(new MyTwigExtension()); - return $twig; - })); +.. code-block:: php -Fetching the service creation function --------------------------------------- + use Pimple\Container; -When you access an object, Pimple automatically calls the anonymous function -that you defined, which creates the service object for you. If you want to get -raw access to this function, you can use the ``raw()`` method:: - - $container['session'] = $container->share(function ($c) { - return new Session($c['session_storage']); - }); - - $sessionFunction = $container->raw('session'); - -Packaging a Container for reusability -------------------------------------- - -If you use the same libraries over and over, you might want to create reusable -containers. Creating a reusable container is as simple as creating a class -that extends ``Pimple``, and configuring it in the constructor:: - - class SomeContainer extends Pimple + class FooProvider implements Pimple\ServiceProviderInterface { - public function __construct() + public function register(Container $pimple) { - $this['parameter'] = 'foo'; - $this['object'] = function () { return stdClass(); }; + // register some services and parameters + // on $pimple } } -Using this container from your own is as easy as it can get:: +Then, register the provider on a Container: - $container = new Pimple(); +.. code-block:: php - // define your project parameters and services - // ... + $pimple->register(new FooProvider()); - // embed the SomeContainer container - $container['embedded'] = $container->share(function () { return new SomeContainer(); }); +Fetching the Service Creation Function +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - // configure it - $container['embedded']['parameter'] = 'bar'; +When you access an object, Pimple automatically calls the anonymous function +that you defined, which creates the service object for you. If you want to get +raw access to this function, you can use the ``raw()`` method: - // use it - $container['embedded']['object']->...; +.. code-block:: php -.. _Download it: https://github.com/fabpot/Pimple + $container['session'] = function ($c) { + return new Session($c['session_storage']); + }; + + $sessionFunction = $container->raw('session'); + +PSR-11 compatibility +-------------------- + +For historical reasons, the ``Container`` class does not implement the PSR-11 +``ContainerInterface``. However, Pimple provides a helper class that will let +you decouple your code from the Pimple container class. + +The PSR-11 container class +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The ``Pimple\Psr11\Container`` class lets you access the content of an +underlying Pimple container using ``Psr\Container\ContainerInterface`` +methods: + +.. code-block:: php + + use Pimple\Container; + use Pimple\Psr11\Container as PsrContainer; + + $container = new Container(); + $container['service'] = function ($c) { + return new Service(); + }; + $psr11 = new PsrContainer($container); + + $controller = function (PsrContainer $container) { + $service = $container->get('service'); + }; + $controller($psr11); + +Using the PSR-11 ServiceLocator +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Sometimes, a service needs access to several other services without being sure +that all of them will actually be used. In those cases, you may want the +instantiation of the services to be lazy. + +The traditional solution is to inject the entire service container to get only +the services really needed. However, this is not recommended because it gives +services a too broad access to the rest of the application and it hides their +actual dependencies. + +The ``ServiceLocator`` is intended to solve this problem by giving access to a +set of predefined services while instantiating them only when actually needed. + +It also allows you to make your services available under a different name than +the one used to register them. For instance, you may want to use an object +that expects an instance of ``EventDispatcherInterface`` to be available under +the name ``event_dispatcher`` while your event dispatcher has been +registered under the name ``dispatcher``: + +.. code-block:: php + + use Monolog\Logger; + use Pimple\Psr11\ServiceLocator; + use Psr\Container\ContainerInterface; + use Symfony\Component\EventDispatcher\EventDispatcher; + + class MyService + { + /** + * "logger" must be an instance of Psr\Log\LoggerInterface + * "event_dispatcher" must be an instance of Symfony\Component\EventDispatcher\EventDispatcherInterface + */ + private $services; + + public function __construct(ContainerInterface $services) + { + $this->services = $services; + } + } + + $container['logger'] = function ($c) { + return new Monolog\Logger(); + }; + $container['dispatcher'] = function () { + return new EventDispatcher(); + }; + + $container['service'] = function ($c) { + $locator = new ServiceLocator($c, array('logger', 'event_dispatcher' => 'dispatcher')); + + return new MyService($locator); + }; + +Referencing a Collection of Services Lazily +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Passing a collection of services instances in an array may prove inefficient +if the class that consumes the collection only needs to iterate over it at a +later stage, when one of its method is called. It can also lead to problems +if there is a circular dependency between one of the services stored in the +collection and the class that consumes it. + +The ``ServiceIterator`` class helps you solve these issues. It receives a +list of service names during instantiation and will retrieve the services +when iterated over: + +.. code-block:: php + + use Pimple\Container; + use Pimple\ServiceIterator; + + class AuthorizationService + { + private $voters; + + public function __construct($voters) + { + $this->voters = $voters; + } + + public function canAccess($resource) + { + foreach ($this->voters as $voter) { + if (true === $voter->canAccess($resource) { + return true; + } + } + + return false; + } + } + + $container = new Container(); + + $container['voter1'] = function ($c) { + return new SomeVoter(); + } + $container['voter2'] = function ($c) { + return new SomeOtherVoter($c['auth']); + } + $container['auth'] = function ($c) { + return new AuthorizationService(new ServiceIterator($c, array('voter1', 'voter2')); + } + +.. _Pimple 1.x documentation: https://github.com/silexphp/Pimple/tree/1.1 diff --git a/lib/silex/vendor/pimple/pimple/composer.json b/lib/silex/vendor/pimple/pimple/composer.json index d95c8c521..dabf190a9 100644 --- a/lib/silex/vendor/pimple/pimple/composer.json +++ b/lib/silex/vendor/pimple/pimple/composer.json @@ -1,7 +1,7 @@ { "name": "pimple/pimple", "type": "library", - "description": "Pimple is a simple Dependency Injection Container for PHP 5.3", + "description": "Pimple, a simple Dependency Injection Container", "keywords": ["dependency injection", "container"], "homepage": "http://pimple.sensiolabs.org", "license": "MIT", @@ -12,14 +12,18 @@ } ], "require": { - "php": ">=5.3.0" + "php": ">=5.3.0", + "psr/container": "^1.0" + }, + "require-dev": { + "symfony/phpunit-bridge": "^3.2" }, "autoload": { - "psr-0": { "Pimple": "lib/" } + "psr-0": { "Pimple": "src/" } }, "extra": { "branch-alias": { - "dev-master": "1.1.x-dev" + "dev-master": "3.2.x-dev" } } -} \ No newline at end of file +} diff --git a/lib/silex/vendor/pimple/pimple/ext/pimple/.gitignore b/lib/silex/vendor/pimple/pimple/ext/pimple/.gitignore new file mode 100644 index 000000000..1861088ac --- /dev/null +++ b/lib/silex/vendor/pimple/pimple/ext/pimple/.gitignore @@ -0,0 +1,30 @@ +*.sw* +.deps +Makefile +Makefile.fragments +Makefile.global +Makefile.objects +acinclude.m4 +aclocal.m4 +build/ +config.cache +config.guess +config.h +config.h.in +config.log +config.nice +config.status +config.sub +configure +configure.in +install-sh +libtool +ltmain.sh +missing +mkinstalldirs +run-tests.php +*.loT +.libs/ +modules/ +*.la +*.lo diff --git a/lib/silex/vendor/pimple/pimple/ext/pimple/README.md b/lib/silex/vendor/pimple/pimple/ext/pimple/README.md new file mode 100644 index 000000000..7b39eb292 --- /dev/null +++ b/lib/silex/vendor/pimple/pimple/ext/pimple/README.md @@ -0,0 +1,12 @@ +This is Pimple 2 implemented in C + +* PHP >= 5.3 +* Not tested under Windows, might work + +Install +======= + + > phpize + > ./configure + > make + > make install diff --git a/lib/silex/vendor/pimple/pimple/ext/pimple/config.m4 b/lib/silex/vendor/pimple/pimple/ext/pimple/config.m4 new file mode 100644 index 000000000..3a6e9aaeb --- /dev/null +++ b/lib/silex/vendor/pimple/pimple/ext/pimple/config.m4 @@ -0,0 +1,63 @@ +dnl $Id$ +dnl config.m4 for extension pimple + +dnl Comments in this file start with the string 'dnl'. +dnl Remove where necessary. This file will not work +dnl without editing. + +dnl If your extension references something external, use with: + +dnl PHP_ARG_WITH(pimple, for pimple support, +dnl Make sure that the comment is aligned: +dnl [ --with-pimple Include pimple support]) + +dnl Otherwise use enable: + +PHP_ARG_ENABLE(pimple, whether to enable pimple support, +dnl Make sure that the comment is aligned: +[ --enable-pimple Enable pimple support]) + +if test "$PHP_PIMPLE" != "no"; then + dnl Write more examples of tests here... + + dnl # --with-pimple -> check with-path + dnl SEARCH_PATH="/usr/local /usr" # you might want to change this + dnl SEARCH_FOR="/include/pimple.h" # you most likely want to change this + dnl if test -r $PHP_PIMPLE/$SEARCH_FOR; then # path given as parameter + dnl PIMPLE_DIR=$PHP_PIMPLE + dnl else # search default path list + dnl AC_MSG_CHECKING([for pimple files in default path]) + dnl for i in $SEARCH_PATH ; do + dnl if test -r $i/$SEARCH_FOR; then + dnl PIMPLE_DIR=$i + dnl AC_MSG_RESULT(found in $i) + dnl fi + dnl done + dnl fi + dnl + dnl if test -z "$PIMPLE_DIR"; then + dnl AC_MSG_RESULT([not found]) + dnl AC_MSG_ERROR([Please reinstall the pimple distribution]) + dnl fi + + dnl # --with-pimple -> add include path + dnl PHP_ADD_INCLUDE($PIMPLE_DIR/include) + + dnl # --with-pimple -> check for lib and symbol presence + dnl LIBNAME=pimple # you may want to change this + dnl LIBSYMBOL=pimple # you most likely want to change this + + dnl PHP_CHECK_LIBRARY($LIBNAME,$LIBSYMBOL, + dnl [ + dnl PHP_ADD_LIBRARY_WITH_PATH($LIBNAME, $PIMPLE_DIR/lib, PIMPLE_SHARED_LIBADD) + dnl AC_DEFINE(HAVE_PIMPLELIB,1,[ ]) + dnl ],[ + dnl AC_MSG_ERROR([wrong pimple lib version or lib not found]) + dnl ],[ + dnl -L$PIMPLE_DIR/lib -lm + dnl ]) + dnl + dnl PHP_SUBST(PIMPLE_SHARED_LIBADD) + + PHP_NEW_EXTENSION(pimple, pimple.c, $ext_shared) +fi diff --git a/lib/silex/vendor/pimple/pimple/ext/pimple/config.w32 b/lib/silex/vendor/pimple/pimple/ext/pimple/config.w32 new file mode 100644 index 000000000..39857b325 --- /dev/null +++ b/lib/silex/vendor/pimple/pimple/ext/pimple/config.w32 @@ -0,0 +1,13 @@ +// $Id$ +// vim:ft=javascript + +// If your extension references something external, use ARG_WITH +// ARG_WITH("pimple", "for pimple support", "no"); + +// Otherwise, use ARG_ENABLE +// ARG_ENABLE("pimple", "enable pimple support", "no"); + +if (PHP_PIMPLE != "no") { + EXTENSION("pimple", "pimple.c"); +} + diff --git a/lib/silex/vendor/pimple/pimple/ext/pimple/php_pimple.h b/lib/silex/vendor/pimple/pimple/ext/pimple/php_pimple.h new file mode 100644 index 000000000..eed7c1731 --- /dev/null +++ b/lib/silex/vendor/pimple/pimple/ext/pimple/php_pimple.h @@ -0,0 +1,137 @@ + +/* + * This file is part of Pimple. + * + * Copyright (c) 2014 Fabien Potencier + * + * 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. + */ + +#ifndef PHP_PIMPLE_H +#define PHP_PIMPLE_H + +extern zend_module_entry pimple_module_entry; +#define phpext_pimple_ptr &pimple_module_entry + +#ifdef PHP_WIN32 +# define PHP_PIMPLE_API __declspec(dllexport) +#elif defined(__GNUC__) && __GNUC__ >= 4 +# define PHP_PIMPLE_API __attribute__ ((visibility("default"))) +#else +# define PHP_PIMPLE_API +#endif + +#ifdef ZTS +#include "TSRM.h" +#endif + +#define PIMPLE_VERSION "3.2.3-DEV" + +#define PIMPLE_NS "Pimple" +#define PSR_CONTAINER_NS "Psr\\Container" +#define PIMPLE_EXCEPTION_NS "Pimple\\Exception" + +#define PIMPLE_DEFAULT_ZVAL_CACHE_NUM 5 +#define PIMPLE_DEFAULT_ZVAL_VALUES_NUM 10 + +#define PIMPLE_DEPRECATE do { \ + int er = EG(error_reporting); \ + EG(error_reporting) = 0;\ + php_error(E_DEPRECATED, "The Pimple C extension is deprecated since version 3.1 and will be removed in 4.0."); \ + EG(error_reporting) = er; \ +} while (0); + +zend_module_entry *get_module(void); + +PHP_MINIT_FUNCTION(pimple); +PHP_MINFO_FUNCTION(pimple); + +PHP_METHOD(FrozenServiceException, __construct); +PHP_METHOD(InvalidServiceIdentifierException, __construct); +PHP_METHOD(UnknownIdentifierException, __construct); + +PHP_METHOD(Pimple, __construct); +PHP_METHOD(Pimple, factory); +PHP_METHOD(Pimple, protect); +PHP_METHOD(Pimple, raw); +PHP_METHOD(Pimple, extend); +PHP_METHOD(Pimple, keys); +PHP_METHOD(Pimple, register); +PHP_METHOD(Pimple, offsetSet); +PHP_METHOD(Pimple, offsetUnset); +PHP_METHOD(Pimple, offsetGet); +PHP_METHOD(Pimple, offsetExists); + +PHP_METHOD(PimpleClosure, invoker); + +typedef struct _pimple_bucket_value { + zval *value; /* Must be the first element */ + zval *raw; + zend_object_handle handle_num; + enum { + PIMPLE_IS_PARAM = 0, + PIMPLE_IS_SERVICE = 2 + } type; + zend_bool initialized; + zend_fcall_info_cache fcc; +} pimple_bucket_value; + +typedef struct _pimple_object { + zend_object zobj; + HashTable values; + HashTable factories; + HashTable protected; +} pimple_object; + +typedef struct _pimple_closure_object { + zend_object zobj; + zval *callable; + zval *factory; +} pimple_closure_object; + +static const char sensiolabs_logo[] = ""; + +static void pimple_exception_call_parent_constructor(zval *this_ptr, const char *format, const char *arg1 TSRMLS_DC); + +static int pimple_zval_to_pimpleval(zval *_zval, pimple_bucket_value *_pimple_bucket_value TSRMLS_DC); +static int pimple_zval_is_valid_callback(zval *_zval, pimple_bucket_value *_pimple_bucket_value TSRMLS_DC); + +static void pimple_bucket_dtor(pimple_bucket_value *bucket); +static void pimple_free_bucket(pimple_bucket_value *bucket); + +static zval *pimple_object_read_dimension(zval *object, zval *offset, int type TSRMLS_DC); +static void pimple_object_write_dimension(zval *object, zval *offset, zval *value TSRMLS_DC); +static int pimple_object_has_dimension(zval *object, zval *offset, int check_empty TSRMLS_DC); +static void pimple_object_unset_dimension(zval *object, zval *offset TSRMLS_DC); +static zend_object_value pimple_object_create(zend_class_entry *ce TSRMLS_DC); +static void pimple_free_object_storage(pimple_object *obj TSRMLS_DC); + +static void pimple_closure_free_object_storage(pimple_closure_object *obj TSRMLS_DC); +static zend_object_value pimple_closure_object_create(zend_class_entry *ce TSRMLS_DC); +static zend_function *pimple_closure_get_constructor(zval * TSRMLS_DC); +static int pimple_closure_get_closure(zval *obj, zend_class_entry **ce_ptr, union _zend_function **fptr_ptr, zval **zobj_ptr TSRMLS_DC); + +#ifdef ZTS +#define PIMPLE_G(v) TSRMG(pimple_globals_id, zend_pimple_globals *, v) +#else +#define PIMPLE_G(v) (pimple_globals.v) +#endif + +#endif /* PHP_PIMPLE_H */ + diff --git a/lib/silex/vendor/pimple/pimple/ext/pimple/pimple.c b/lib/silex/vendor/pimple/pimple/ext/pimple/pimple.c new file mode 100644 index 000000000..c80499b39 --- /dev/null +++ b/lib/silex/vendor/pimple/pimple/ext/pimple/pimple.c @@ -0,0 +1,1114 @@ + +/* + * This file is part of Pimple. + * + * Copyright (c) 2014 Fabien Potencier + * + * 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. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "php.h" +#include "php_ini.h" +#include "ext/standard/info.h" +#include "php_pimple.h" +#include "pimple_compat.h" +#include "zend_interfaces.h" +#include "zend.h" +#include "Zend/zend_closures.h" +#include "ext/spl/spl_exceptions.h" +#include "Zend/zend_exceptions.h" +#include "main/php_output.h" +#include "SAPI.h" + +static zend_class_entry *pimple_ce_PsrContainerInterface; +static zend_class_entry *pimple_ce_PsrContainerExceptionInterface; +static zend_class_entry *pimple_ce_PsrNotFoundExceptionInterface; + +static zend_class_entry *pimple_ce_ExpectedInvokableException; +static zend_class_entry *pimple_ce_FrozenServiceException; +static zend_class_entry *pimple_ce_InvalidServiceIdentifierException; +static zend_class_entry *pimple_ce_UnknownIdentifierException; + +static zend_class_entry *pimple_ce; +static zend_object_handlers pimple_object_handlers; +static zend_class_entry *pimple_closure_ce; +static zend_class_entry *pimple_serviceprovider_ce; +static zend_object_handlers pimple_closure_object_handlers; +static zend_internal_function pimple_closure_invoker_function; + +#define FETCH_DIM_HANDLERS_VARS pimple_object *pimple_obj = NULL; \ + ulong index; \ + pimple_obj = (pimple_object *)zend_object_store_get_object(object TSRMLS_CC); \ + +#define PIMPLE_OBJECT_HANDLE_INHERITANCE_OBJECT_HANDLERS do { \ + if (ce != pimple_ce) { \ + zend_hash_find(&ce->function_table, ZEND_STRS("offsetget"), (void **)&function); \ + if (function->common.scope != ce) { /* if the function is not defined in this actual class */ \ + pimple_object_handlers.read_dimension = pimple_object_read_dimension; /* then overwrite the handler to use custom one */ \ + } \ + zend_hash_find(&ce->function_table, ZEND_STRS("offsetset"), (void **)&function); \ + if (function->common.scope != ce) { \ + pimple_object_handlers.write_dimension = pimple_object_write_dimension; \ + } \ + zend_hash_find(&ce->function_table, ZEND_STRS("offsetexists"), (void **)&function); \ + if (function->common.scope != ce) { \ + pimple_object_handlers.has_dimension = pimple_object_has_dimension; \ + } \ + zend_hash_find(&ce->function_table, ZEND_STRS("offsetunset"), (void **)&function); \ + if (function->common.scope != ce) { \ + pimple_object_handlers.unset_dimension = pimple_object_unset_dimension; \ + } \ + } else { \ + pimple_object_handlers.read_dimension = pimple_object_read_dimension; \ + pimple_object_handlers.write_dimension = pimple_object_write_dimension; \ + pimple_object_handlers.has_dimension = pimple_object_has_dimension; \ + pimple_object_handlers.unset_dimension = pimple_object_unset_dimension; \ + }\ + } while(0); + +#define PIMPLE_CALL_CB do { \ + zend_fcall_info_argn(&fci TSRMLS_CC, 1, &object); \ + fci.size = sizeof(fci); \ + fci.object_ptr = retval->fcc.object_ptr; \ + fci.function_name = retval->value; \ + fci.no_separation = 1; \ + fci.retval_ptr_ptr = &retval_ptr_ptr; \ +\ + zend_call_function(&fci, &retval->fcc TSRMLS_CC); \ + efree(fci.params); \ + if (EG(exception)) { \ + return EG(uninitialized_zval_ptr); \ + } \ + } while(0); + + +/* Psr\Container\ContainerInterface */ +ZEND_BEGIN_ARG_INFO_EX(arginfo_pimple_PsrContainerInterface_get, 0, 0, 1) +ZEND_ARG_INFO(0, id) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_pimple_PsrContainerInterface_has, 0, 0, 1) +ZEND_ARG_INFO(0, id) +ZEND_END_ARG_INFO() + +static const zend_function_entry pimple_ce_PsrContainerInterface_functions[] = { + PHP_ABSTRACT_ME(ContainerInterface, get, arginfo_pimple_PsrContainerInterface_get) + PHP_ABSTRACT_ME(ContainerInterface, has, arginfo_pimple_PsrContainerInterface_has) + PHP_FE_END +}; + +/* Psr\Container\ContainerExceptionInterface */ +static const zend_function_entry pimple_ce_PsrContainerExceptionInterface_functions[] = { + PHP_FE_END +}; + +/* Psr\Container\NotFoundExceptionInterface */ +static const zend_function_entry pimple_ce_PsrNotFoundExceptionInterface_functions[] = { + PHP_FE_END +}; + +/* Pimple\Exception\FrozenServiceException */ +ZEND_BEGIN_ARG_INFO_EX(arginfo_FrozenServiceException___construct, 0, 0, 1) +ZEND_ARG_INFO(0, id) +ZEND_END_ARG_INFO() + +static const zend_function_entry pimple_ce_FrozenServiceException_functions[] = { + PHP_ME(FrozenServiceException, __construct, arginfo_FrozenServiceException___construct, ZEND_ACC_PUBLIC) + PHP_FE_END +}; + +/* Pimple\Exception\InvalidServiceIdentifierException */ +ZEND_BEGIN_ARG_INFO_EX(arginfo_InvalidServiceIdentifierException___construct, 0, 0, 1) +ZEND_ARG_INFO(0, id) +ZEND_END_ARG_INFO() + +static const zend_function_entry pimple_ce_InvalidServiceIdentifierException_functions[] = { + PHP_ME(InvalidServiceIdentifierException, __construct, arginfo_InvalidServiceIdentifierException___construct, ZEND_ACC_PUBLIC) + PHP_FE_END +}; + +/* Pimple\Exception\UnknownIdentifierException */ +ZEND_BEGIN_ARG_INFO_EX(arginfo_UnknownIdentifierException___construct, 0, 0, 1) +ZEND_ARG_INFO(0, id) +ZEND_END_ARG_INFO() + +static const zend_function_entry pimple_ce_UnknownIdentifierException_functions[] = { + PHP_ME(UnknownIdentifierException, __construct, arginfo_UnknownIdentifierException___construct, ZEND_ACC_PUBLIC) + PHP_FE_END +}; + +/* Pimple\Container */ +ZEND_BEGIN_ARG_INFO_EX(arginfo___construct, 0, 0, 0) +ZEND_ARG_ARRAY_INFO(0, value, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_offsetset, 0, 0, 2) +ZEND_ARG_INFO(0, offset) +ZEND_ARG_INFO(0, value) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_offsetget, 0, 0, 1) +ZEND_ARG_INFO(0, offset) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_offsetexists, 0, 0, 1) +ZEND_ARG_INFO(0, offset) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_offsetunset, 0, 0, 1) +ZEND_ARG_INFO(0, offset) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_factory, 0, 0, 1) +ZEND_ARG_INFO(0, callable) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_protect, 0, 0, 1) +ZEND_ARG_INFO(0, callable) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_raw, 0, 0, 1) +ZEND_ARG_INFO(0, id) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_extend, 0, 0, 2) +ZEND_ARG_INFO(0, id) +ZEND_ARG_INFO(0, callable) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_keys, 0, 0, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_register, 0, 0, 1) +ZEND_ARG_OBJ_INFO(0, provider, Pimple\\ServiceProviderInterface, 0) +ZEND_ARG_ARRAY_INFO(0, values, 1) +ZEND_END_ARG_INFO() + +static const zend_function_entry pimple_ce_functions[] = { + PHP_ME(Pimple, __construct, arginfo___construct, ZEND_ACC_PUBLIC) + PHP_ME(Pimple, factory, arginfo_factory, ZEND_ACC_PUBLIC) + PHP_ME(Pimple, protect, arginfo_protect, ZEND_ACC_PUBLIC) + PHP_ME(Pimple, raw, arginfo_raw, ZEND_ACC_PUBLIC) + PHP_ME(Pimple, extend, arginfo_extend, ZEND_ACC_PUBLIC) + PHP_ME(Pimple, keys, arginfo_keys, ZEND_ACC_PUBLIC) + PHP_ME(Pimple, register, arginfo_register, ZEND_ACC_PUBLIC) + + PHP_ME(Pimple, offsetSet, arginfo_offsetset, ZEND_ACC_PUBLIC) + PHP_ME(Pimple, offsetGet, arginfo_offsetget, ZEND_ACC_PUBLIC) + PHP_ME(Pimple, offsetExists, arginfo_offsetexists, ZEND_ACC_PUBLIC) + PHP_ME(Pimple, offsetUnset, arginfo_offsetunset, ZEND_ACC_PUBLIC) + PHP_FE_END +}; + +/* Pimple\ServiceProviderInterface */ +ZEND_BEGIN_ARG_INFO_EX(arginfo_serviceprovider_register, 0, 0, 1) +ZEND_ARG_OBJ_INFO(0, pimple, Pimple\\Container, 0) +ZEND_END_ARG_INFO() + +static const zend_function_entry pimple_serviceprovider_iface_ce_functions[] = { + PHP_ABSTRACT_ME(ServiceProviderInterface, register, arginfo_serviceprovider_register) + PHP_FE_END +}; + +/* parent::__construct(sprintf("Something with %s", $arg1)) */ +static void pimple_exception_call_parent_constructor(zval *this_ptr, const char *format, const char *arg1 TSRMLS_DC) +{ + zend_class_entry *ce = Z_OBJCE_P(this_ptr); + char *message = NULL; + int message_len; + zval *constructor_arg; + + message_len = spprintf(&message, 0, format, arg1); + ALLOC_INIT_ZVAL(constructor_arg); + ZVAL_STRINGL(constructor_arg, message, message_len, 1); + + zend_call_method_with_1_params(&this_ptr, ce, &ce->parent->constructor, "__construct", NULL, constructor_arg); + + efree(message); + zval_ptr_dtor(&constructor_arg); +} + +/** + * Pass a single string parameter to exception constructor and throw + */ +static void pimple_throw_exception_string(zend_class_entry *ce, const char *message, zend_uint message_len TSRMLS_DC) +{ + zval *exception, *param; + + ALLOC_INIT_ZVAL(exception); + object_init_ex(exception, ce); + + ALLOC_INIT_ZVAL(param); + ZVAL_STRINGL(param, message, message_len, 1); + + zend_call_method_with_1_params(&exception, ce, &ce->constructor, "__construct", NULL, param); + + zend_throw_exception_object(exception TSRMLS_CC); + + zval_ptr_dtor(¶m); +} + +static void pimple_closure_free_object_storage(pimple_closure_object *obj TSRMLS_DC) +{ + zend_object_std_dtor(&obj->zobj TSRMLS_CC); + if (obj->factory) { + zval_ptr_dtor(&obj->factory); + } + if (obj->callable) { + zval_ptr_dtor(&obj->callable); + } + efree(obj); +} + +static void pimple_free_object_storage(pimple_object *obj TSRMLS_DC) +{ + zend_hash_destroy(&obj->factories); + zend_hash_destroy(&obj->protected); + zend_hash_destroy(&obj->values); + zend_object_std_dtor(&obj->zobj TSRMLS_CC); + efree(obj); +} + +static void pimple_free_bucket(pimple_bucket_value *bucket) +{ + if (bucket->raw) { + zval_ptr_dtor(&bucket->raw); + } +} + +static zend_object_value pimple_closure_object_create(zend_class_entry *ce TSRMLS_DC) +{ + zend_object_value retval; + pimple_closure_object *pimple_closure_obj = NULL; + + pimple_closure_obj = ecalloc(1, sizeof(pimple_closure_object)); + ZEND_OBJ_INIT(&pimple_closure_obj->zobj, ce); + + pimple_closure_object_handlers.get_constructor = pimple_closure_get_constructor; + retval.handlers = &pimple_closure_object_handlers; + retval.handle = zend_objects_store_put(pimple_closure_obj, (zend_objects_store_dtor_t) zend_objects_destroy_object, (zend_objects_free_object_storage_t) pimple_closure_free_object_storage, NULL TSRMLS_CC); + + return retval; +} + +static zend_function *pimple_closure_get_constructor(zval *obj TSRMLS_DC) +{ + zend_error(E_ERROR, "Pimple\\ContainerClosure is an internal class and cannot be instantiated"); + + return NULL; +} + +static int pimple_closure_get_closure(zval *obj, zend_class_entry **ce_ptr, union _zend_function **fptr_ptr, zval **zobj_ptr TSRMLS_DC) +{ + *zobj_ptr = obj; + *ce_ptr = Z_OBJCE_P(obj); + *fptr_ptr = (zend_function *)&pimple_closure_invoker_function; + + return SUCCESS; +} + +static zend_object_value pimple_object_create(zend_class_entry *ce TSRMLS_DC) +{ + zend_object_value retval; + pimple_object *pimple_obj = NULL; + zend_function *function = NULL; + + pimple_obj = emalloc(sizeof(pimple_object)); + ZEND_OBJ_INIT(&pimple_obj->zobj, ce); + + PIMPLE_OBJECT_HANDLE_INHERITANCE_OBJECT_HANDLERS + + retval.handlers = &pimple_object_handlers; + retval.handle = zend_objects_store_put(pimple_obj, (zend_objects_store_dtor_t) zend_objects_destroy_object, (zend_objects_free_object_storage_t) pimple_free_object_storage, NULL TSRMLS_CC); + + zend_hash_init(&pimple_obj->factories, PIMPLE_DEFAULT_ZVAL_CACHE_NUM, NULL, (dtor_func_t)pimple_bucket_dtor, 0); + zend_hash_init(&pimple_obj->protected, PIMPLE_DEFAULT_ZVAL_CACHE_NUM, NULL, (dtor_func_t)pimple_bucket_dtor, 0); + zend_hash_init(&pimple_obj->values, PIMPLE_DEFAULT_ZVAL_VALUES_NUM, NULL, (dtor_func_t)pimple_bucket_dtor, 0); + + return retval; +} + +static void pimple_object_write_dimension(zval *object, zval *offset, zval *value TSRMLS_DC) +{ + FETCH_DIM_HANDLERS_VARS + + pimple_bucket_value pimple_value = {0}, *found_value = NULL; + ulong hash; + + pimple_zval_to_pimpleval(value, &pimple_value TSRMLS_CC); + + if (!offset) {/* $p[] = 'foo' when not overloaded */ + zend_hash_next_index_insert(&pimple_obj->values, (void *)&pimple_value, sizeof(pimple_bucket_value), NULL); + Z_ADDREF_P(value); + return; + } + + switch (Z_TYPE_P(offset)) { + case IS_STRING: + hash = zend_hash_func(Z_STRVAL_P(offset), Z_STRLEN_P(offset)+1); + zend_hash_quick_find(&pimple_obj->values, Z_STRVAL_P(offset), Z_STRLEN_P(offset)+1, hash, (void **)&found_value); + if (found_value && found_value->type == PIMPLE_IS_SERVICE && found_value->initialized == 1) { + pimple_free_bucket(&pimple_value); + pimple_throw_exception_string(pimple_ce_FrozenServiceException, Z_STRVAL_P(offset), Z_STRLEN_P(offset) TSRMLS_CC); + return; + } + if (zend_hash_quick_update(&pimple_obj->values, Z_STRVAL_P(offset), Z_STRLEN_P(offset)+1, hash, (void *)&pimple_value, sizeof(pimple_bucket_value), NULL) == FAILURE) { + pimple_free_bucket(&pimple_value); + return; + } + Z_ADDREF_P(value); + break; + case IS_DOUBLE: + case IS_BOOL: + case IS_LONG: + if (Z_TYPE_P(offset) == IS_DOUBLE) { + index = (ulong)Z_DVAL_P(offset); + } else { + index = Z_LVAL_P(offset); + } + zend_hash_index_find(&pimple_obj->values, index, (void **)&found_value); + if (found_value && found_value->type == PIMPLE_IS_SERVICE && found_value->initialized == 1) { + pimple_free_bucket(&pimple_value); + convert_to_string(offset); + pimple_throw_exception_string(pimple_ce_FrozenServiceException, Z_STRVAL_P(offset), Z_STRLEN_P(offset) TSRMLS_CC); + return; + } + if (zend_hash_index_update(&pimple_obj->values, index, (void *)&pimple_value, sizeof(pimple_bucket_value), NULL) == FAILURE) { + pimple_free_bucket(&pimple_value); + return; + } + Z_ADDREF_P(value); + break; + case IS_NULL: /* $p[] = 'foo' when overloaded */ + zend_hash_next_index_insert(&pimple_obj->values, (void *)&pimple_value, sizeof(pimple_bucket_value), NULL); + Z_ADDREF_P(value); + break; + default: + pimple_free_bucket(&pimple_value); + zend_error(E_WARNING, "Unsupported offset type"); + } +} + +static void pimple_object_unset_dimension(zval *object, zval *offset TSRMLS_DC) +{ + FETCH_DIM_HANDLERS_VARS + + switch (Z_TYPE_P(offset)) { + case IS_STRING: + zend_symtable_del(&pimple_obj->values, Z_STRVAL_P(offset), Z_STRLEN_P(offset)+1); + zend_symtable_del(&pimple_obj->factories, Z_STRVAL_P(offset), Z_STRLEN_P(offset)+1); + zend_symtable_del(&pimple_obj->protected, Z_STRVAL_P(offset), Z_STRLEN_P(offset)+1); + break; + case IS_DOUBLE: + case IS_BOOL: + case IS_LONG: + if (Z_TYPE_P(offset) == IS_DOUBLE) { + index = (ulong)Z_DVAL_P(offset); + } else { + index = Z_LVAL_P(offset); + } + zend_hash_index_del(&pimple_obj->values, index); + zend_hash_index_del(&pimple_obj->factories, index); + zend_hash_index_del(&pimple_obj->protected, index); + break; + default: + zend_error(E_WARNING, "Unsupported offset type"); + } +} + +static int pimple_object_has_dimension(zval *object, zval *offset, int check_empty TSRMLS_DC) +{ + FETCH_DIM_HANDLERS_VARS + + pimple_bucket_value *retval = NULL; + + switch (Z_TYPE_P(offset)) { + case IS_STRING: + if (zend_symtable_find(&pimple_obj->values, Z_STRVAL_P(offset), Z_STRLEN_P(offset)+1, (void **)&retval) == SUCCESS) { + switch (check_empty) { + case 0: /* isset */ + return 1; /* Differs from PHP behavior (Z_TYPE_P(retval->value) != IS_NULL;) */ + case 1: /* empty */ + default: + return zend_is_true(retval->value); + } + } + return 0; + break; + case IS_DOUBLE: + case IS_BOOL: + case IS_LONG: + if (Z_TYPE_P(offset) == IS_DOUBLE) { + index = (ulong)Z_DVAL_P(offset); + } else { + index = Z_LVAL_P(offset); + } + if (zend_hash_index_find(&pimple_obj->values, index, (void **)&retval) == SUCCESS) { + switch (check_empty) { + case 0: /* isset */ + return 1; /* Differs from PHP behavior (Z_TYPE_P(retval->value) != IS_NULL;)*/ + case 1: /* empty */ + default: + return zend_is_true(retval->value); + } + } + return 0; + break; + default: + zend_error(E_WARNING, "Unsupported offset type"); + return 0; + } +} + +static zval *pimple_object_read_dimension(zval *object, zval *offset, int type TSRMLS_DC) +{ + FETCH_DIM_HANDLERS_VARS + + pimple_bucket_value *retval = NULL; + zend_fcall_info fci = {0}; + zval *retval_ptr_ptr = NULL; + + switch (Z_TYPE_P(offset)) { + case IS_STRING: + if (zend_symtable_find(&pimple_obj->values, Z_STRVAL_P(offset), Z_STRLEN_P(offset)+1, (void **)&retval) == FAILURE) { + pimple_throw_exception_string(pimple_ce_UnknownIdentifierException, Z_STRVAL_P(offset), Z_STRLEN_P(offset) TSRMLS_CC); + + return EG(uninitialized_zval_ptr); + } + break; + case IS_DOUBLE: + case IS_BOOL: + case IS_LONG: + if (Z_TYPE_P(offset) == IS_DOUBLE) { + index = (ulong)Z_DVAL_P(offset); + } else { + index = Z_LVAL_P(offset); + } + if (zend_hash_index_find(&pimple_obj->values, index, (void **)&retval) == FAILURE) { + return EG(uninitialized_zval_ptr); + } + break; + case IS_NULL: /* $p[][3] = 'foo' first dim access */ + return EG(uninitialized_zval_ptr); + break; + default: + zend_error(E_WARNING, "Unsupported offset type"); + return EG(uninitialized_zval_ptr); + } + + if(retval->type == PIMPLE_IS_PARAM) { + return retval->value; + } + + if (zend_hash_index_exists(&pimple_obj->protected, retval->handle_num)) { + /* Service is protected, return the value every time */ + return retval->value; + } + + if (zend_hash_index_exists(&pimple_obj->factories, retval->handle_num)) { + /* Service is a factory, call it every time and never cache its result */ + PIMPLE_CALL_CB + Z_DELREF_P(retval_ptr_ptr); /* fetch dim addr will increment refcount */ + return retval_ptr_ptr; + } + + if (retval->initialized == 1) { + /* Service has already been called, return its cached value */ + return retval->value; + } + + ALLOC_INIT_ZVAL(retval->raw); + MAKE_COPY_ZVAL(&retval->value, retval->raw); + + PIMPLE_CALL_CB + + retval->initialized = 1; + zval_ptr_dtor(&retval->value); + retval->value = retval_ptr_ptr; + + return retval->value; +} + +static int pimple_zval_is_valid_callback(zval *_zval, pimple_bucket_value *_pimple_bucket_value TSRMLS_DC) +{ + if (Z_TYPE_P(_zval) != IS_OBJECT) { + return FAILURE; + } + + if (_pimple_bucket_value->fcc.called_scope) { + return SUCCESS; + } + + if (Z_OBJ_HANDLER_P(_zval, get_closure) && Z_OBJ_HANDLER_P(_zval, get_closure)(_zval, &_pimple_bucket_value->fcc.calling_scope, &_pimple_bucket_value->fcc.function_handler, &_pimple_bucket_value->fcc.object_ptr TSRMLS_CC) == SUCCESS) { + _pimple_bucket_value->fcc.called_scope = _pimple_bucket_value->fcc.calling_scope; + return SUCCESS; + } else { + return FAILURE; + } +} + +static int pimple_zval_to_pimpleval(zval *_zval, pimple_bucket_value *_pimple_bucket_value TSRMLS_DC) +{ + _pimple_bucket_value->value = _zval; + + if (Z_TYPE_P(_zval) != IS_OBJECT) { + return PIMPLE_IS_PARAM; + } + + if (pimple_zval_is_valid_callback(_zval, _pimple_bucket_value TSRMLS_CC) == SUCCESS) { + _pimple_bucket_value->type = PIMPLE_IS_SERVICE; + _pimple_bucket_value->handle_num = Z_OBJ_HANDLE_P(_zval); + } + + return PIMPLE_IS_SERVICE; +} + +static void pimple_bucket_dtor(pimple_bucket_value *bucket) +{ + zval_ptr_dtor(&bucket->value); + pimple_free_bucket(bucket); +} + +PHP_METHOD(FrozenServiceException, __construct) +{ + char *id = NULL; + int id_len; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &id, &id_len) == FAILURE) { + return; + } + pimple_exception_call_parent_constructor(getThis(), "Cannot override frozen service \"%s\".", id TSRMLS_CC); +} + +PHP_METHOD(InvalidServiceIdentifierException, __construct) +{ + char *id = NULL; + int id_len; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &id, &id_len) == FAILURE) { + return; + } + pimple_exception_call_parent_constructor(getThis(), "Identifier \"%s\" does not contain an object definition.", id TSRMLS_CC); +} + +PHP_METHOD(UnknownIdentifierException, __construct) +{ + char *id = NULL; + int id_len; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &id, &id_len) == FAILURE) { + return; + } + pimple_exception_call_parent_constructor(getThis(), "Identifier \"%s\" is not defined.", id TSRMLS_CC); +} + +PHP_METHOD(Pimple, protect) +{ + zval *protected = NULL; + pimple_object *pobj = NULL; + pimple_bucket_value bucket = {0}; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &protected) == FAILURE) { + return; + } + + if (pimple_zval_is_valid_callback(protected, &bucket TSRMLS_CC) == FAILURE) { + pimple_free_bucket(&bucket); + zend_throw_exception(pimple_ce_ExpectedInvokableException, "Callable is not a Closure or invokable object.", 0 TSRMLS_CC); + return; + } + + pimple_zval_to_pimpleval(protected, &bucket TSRMLS_CC); + pobj = (pimple_object *)zend_object_store_get_object(getThis() TSRMLS_CC); + + if (zend_hash_index_update(&pobj->protected, bucket.handle_num, (void *)&bucket, sizeof(pimple_bucket_value), NULL) == SUCCESS) { + Z_ADDREF_P(protected); + RETURN_ZVAL(protected, 1 , 0); + } else { + pimple_free_bucket(&bucket); + } + RETURN_FALSE; +} + +PHP_METHOD(Pimple, raw) +{ + zval *offset = NULL; + pimple_object *pobj = NULL; + pimple_bucket_value *value = NULL; + ulong index; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &offset) == FAILURE) { + return; + } + + pobj = zend_object_store_get_object(getThis() TSRMLS_CC); + + switch (Z_TYPE_P(offset)) { + case IS_STRING: + if (zend_symtable_find(&pobj->values, Z_STRVAL_P(offset), Z_STRLEN_P(offset)+1, (void *)&value) == FAILURE) { + pimple_throw_exception_string(pimple_ce_UnknownIdentifierException, Z_STRVAL_P(offset), Z_STRLEN_P(offset) TSRMLS_CC); + RETURN_NULL(); + } + break; + case IS_DOUBLE: + case IS_BOOL: + case IS_LONG: + if (Z_TYPE_P(offset) == IS_DOUBLE) { + index = (ulong)Z_DVAL_P(offset); + } else { + index = Z_LVAL_P(offset); + } + if (zend_hash_index_find(&pobj->values, index, (void *)&value) == FAILURE) { + RETURN_NULL(); + } + break; + case IS_NULL: + default: + zend_error(E_WARNING, "Unsupported offset type"); + } + + if (value->raw) { + RETVAL_ZVAL(value->raw, 1, 0); + } else { + RETVAL_ZVAL(value->value, 1, 0); + } +} + +PHP_METHOD(Pimple, extend) +{ + zval *offset = NULL, *callable = NULL, *pimple_closure_obj = NULL; + pimple_bucket_value bucket = {0}, *value = NULL; + pimple_object *pobj = NULL; + pimple_closure_object *pcobj = NULL; + ulong index; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zz", &offset, &callable) == FAILURE) { + return; + } + + pobj = zend_object_store_get_object(getThis() TSRMLS_CC); + + switch (Z_TYPE_P(offset)) { + case IS_STRING: + if (zend_symtable_find(&pobj->values, Z_STRVAL_P(offset), Z_STRLEN_P(offset)+1, (void *)&value) == FAILURE) { + pimple_throw_exception_string(pimple_ce_UnknownIdentifierException, Z_STRVAL_P(offset), Z_STRLEN_P(offset) TSRMLS_CC); + RETURN_NULL(); + } + + if (value->type != PIMPLE_IS_SERVICE) { + pimple_throw_exception_string(pimple_ce_InvalidServiceIdentifierException, Z_STRVAL_P(offset), Z_STRLEN_P(offset) TSRMLS_CC); + RETURN_NULL(); + } + if (zend_hash_index_exists(&pobj->protected, value->handle_num)) { + int er = EG(error_reporting); + EG(error_reporting) = 0; + php_error(E_DEPRECATED, "How Pimple behaves when extending protected closures will be fixed in Pimple 4. Are you sure \"%s\" should be protected?", Z_STRVAL_P(offset)); + EG(error_reporting) = er; + } + break; + case IS_DOUBLE: + case IS_BOOL: + case IS_LONG: + if (Z_TYPE_P(offset) == IS_DOUBLE) { + index = (ulong)Z_DVAL_P(offset); + } else { + index = Z_LVAL_P(offset); + } + if (zend_hash_index_find(&pobj->values, index, (void *)&value) == FAILURE) { + convert_to_string(offset); + pimple_throw_exception_string(pimple_ce_UnknownIdentifierException, Z_STRVAL_P(offset), Z_STRLEN_P(offset) TSRMLS_CC); + RETURN_NULL(); + } + if (value->type != PIMPLE_IS_SERVICE) { + convert_to_string(offset); + pimple_throw_exception_string(pimple_ce_InvalidServiceIdentifierException, Z_STRVAL_P(offset), Z_STRLEN_P(offset) TSRMLS_CC); + RETURN_NULL(); + } + if (zend_hash_index_exists(&pobj->protected, value->handle_num)) { + int er = EG(error_reporting); + EG(error_reporting) = 0; + php_error(E_DEPRECATED, "How Pimple behaves when extending protected closures will be fixed in Pimple 4. Are you sure \"%ld\" should be protected?", index); + EG(error_reporting) = er; + } + break; + case IS_NULL: + default: + zend_error(E_WARNING, "Unsupported offset type"); + } + + if (pimple_zval_is_valid_callback(callable, &bucket TSRMLS_CC) == FAILURE) { + pimple_free_bucket(&bucket); + zend_throw_exception(pimple_ce_ExpectedInvokableException, "Extension service definition is not a Closure or invokable object.", 0 TSRMLS_CC); + RETURN_NULL(); + } + pimple_free_bucket(&bucket); + + ALLOC_INIT_ZVAL(pimple_closure_obj); + object_init_ex(pimple_closure_obj, pimple_closure_ce); + + pcobj = zend_object_store_get_object(pimple_closure_obj TSRMLS_CC); + pcobj->callable = callable; + pcobj->factory = value->value; + Z_ADDREF_P(callable); + Z_ADDREF_P(value->value); + + if (zend_hash_index_exists(&pobj->factories, value->handle_num)) { + pimple_zval_to_pimpleval(pimple_closure_obj, &bucket TSRMLS_CC); + zend_hash_index_del(&pobj->factories, value->handle_num); + zend_hash_index_update(&pobj->factories, bucket.handle_num, (void *)&bucket, sizeof(pimple_bucket_value), NULL); + Z_ADDREF_P(pimple_closure_obj); + } + + pimple_object_write_dimension(getThis(), offset, pimple_closure_obj TSRMLS_CC); + + RETVAL_ZVAL(pimple_closure_obj, 1, 1); +} + +PHP_METHOD(Pimple, keys) +{ + HashPosition pos; + pimple_object *pobj = NULL; + zval **value = NULL; + zval *endval = NULL; + char *str_index = NULL; + int str_len; + ulong num_index; + + if (zend_parse_parameters_none() == FAILURE) { + return; + } + + pobj = zend_object_store_get_object(getThis() TSRMLS_CC); + array_init_size(return_value, zend_hash_num_elements(&pobj->values)); + + zend_hash_internal_pointer_reset_ex(&pobj->values, &pos); + + while(zend_hash_get_current_data_ex(&pobj->values, (void **)&value, &pos) == SUCCESS) { + MAKE_STD_ZVAL(endval); + switch (zend_hash_get_current_key_ex(&pobj->values, &str_index, (uint *)&str_len, &num_index, 0, &pos)) { + case HASH_KEY_IS_STRING: + ZVAL_STRINGL(endval, str_index, str_len - 1, 1); + zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &endval, sizeof(zval *), NULL); + break; + case HASH_KEY_IS_LONG: + ZVAL_LONG(endval, num_index); + zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &endval, sizeof(zval *), NULL); + break; + } + zend_hash_move_forward_ex(&pobj->values, &pos); + } +} + +PHP_METHOD(Pimple, factory) +{ + zval *factory = NULL; + pimple_object *pobj = NULL; + pimple_bucket_value bucket = {0}; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &factory) == FAILURE) { + return; + } + + if (pimple_zval_is_valid_callback(factory, &bucket TSRMLS_CC) == FAILURE) { + pimple_free_bucket(&bucket); + zend_throw_exception(pimple_ce_ExpectedInvokableException, "Service definition is not a Closure or invokable object.", 0 TSRMLS_CC); + return; + } + + pimple_zval_to_pimpleval(factory, &bucket TSRMLS_CC); + pobj = (pimple_object *)zend_object_store_get_object(getThis() TSRMLS_CC); + + if (zend_hash_index_update(&pobj->factories, bucket.handle_num, (void *)&bucket, sizeof(pimple_bucket_value), NULL) == SUCCESS) { + Z_ADDREF_P(factory); + RETURN_ZVAL(factory, 1 , 0); + } else { + pimple_free_bucket(&bucket); + } + + RETURN_FALSE; +} + +PHP_METHOD(Pimple, offsetSet) +{ + zval *offset = NULL, *value = NULL; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zz", &offset, &value) == FAILURE) { + return; + } + + pimple_object_write_dimension(getThis(), offset, value TSRMLS_CC); +} + +PHP_METHOD(Pimple, offsetGet) +{ + zval *offset = NULL, *retval = NULL; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &offset) == FAILURE) { + return; + } + + retval = pimple_object_read_dimension(getThis(), offset, 0 TSRMLS_CC); + + RETVAL_ZVAL(retval, 1, 0); +} + +PHP_METHOD(Pimple, offsetUnset) +{ + zval *offset = NULL; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &offset) == FAILURE) { + return; + } + + pimple_object_unset_dimension(getThis(), offset TSRMLS_CC); +} + +PHP_METHOD(Pimple, offsetExists) +{ + zval *offset = NULL; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &offset) == FAILURE) { + return; + } + + RETVAL_BOOL(pimple_object_has_dimension(getThis(), offset, 1 TSRMLS_CC)); +} + +PHP_METHOD(Pimple, register) +{ + zval *provider; + zval **data; + zval *retval = NULL; + zval key; + + HashTable *array = NULL; + HashPosition pos; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O|h", &provider, pimple_serviceprovider_ce, &array) == FAILURE) { + return; + } + + RETVAL_ZVAL(getThis(), 1, 0); + + zend_call_method_with_1_params(&provider, Z_OBJCE_P(provider), NULL, "register", &retval, getThis()); + + if (retval) { + zval_ptr_dtor(&retval); + } + + if (!array) { + return; + } + + zend_hash_internal_pointer_reset_ex(array, &pos); + + while(zend_hash_get_current_data_ex(array, (void **)&data, &pos) == SUCCESS) { + zend_hash_get_current_key_zval_ex(array, &key, &pos); + pimple_object_write_dimension(getThis(), &key, *data TSRMLS_CC); + zend_hash_move_forward_ex(array, &pos); + } +} + +PHP_METHOD(Pimple, __construct) +{ + zval *values = NULL, **pData = NULL, offset; + HashPosition pos; + char *str_index = NULL; + zend_uint str_length; + ulong num_index; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|a!", &values) == FAILURE) { + return; + } + + PIMPLE_DEPRECATE + + if (!values) { + return; + } + + zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(values), &pos); + while (zend_hash_has_more_elements_ex(Z_ARRVAL_P(values), &pos) == SUCCESS) { + zend_hash_get_current_data_ex(Z_ARRVAL_P(values), (void **)&pData, &pos); + zend_hash_get_current_key_ex(Z_ARRVAL_P(values), &str_index, &str_length, &num_index, 0, &pos); + INIT_ZVAL(offset); + if (zend_hash_get_current_key_type_ex(Z_ARRVAL_P(values), &pos) == HASH_KEY_IS_LONG) { + ZVAL_LONG(&offset, num_index); + } else { + ZVAL_STRINGL(&offset, str_index, (str_length - 1), 0); + } + pimple_object_write_dimension(getThis(), &offset, *pData TSRMLS_CC); + zend_hash_move_forward_ex(Z_ARRVAL_P(values), &pos); + } +} + +/* + * This is PHP code snippet handling extend()s calls : + + $extended = function ($c) use ($callable, $factory) { + return $callable($factory($c), $c); + }; + + */ +PHP_METHOD(PimpleClosure, invoker) +{ + pimple_closure_object *pcobj = NULL; + zval *arg = NULL, *retval = NULL, *newretval = NULL; + zend_fcall_info fci = {0}; + zval **args[2]; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &arg) == FAILURE) { + return; + } + + pcobj = zend_object_store_get_object(getThis() TSRMLS_CC); + + fci.function_name = pcobj->factory; + args[0] = &arg; + zend_fcall_info_argp(&fci TSRMLS_CC, 1, args); + fci.retval_ptr_ptr = &retval; + fci.size = sizeof(fci); + + if (zend_call_function(&fci, NULL TSRMLS_CC) == FAILURE || EG(exception)) { + efree(fci.params); + return; /* Should here return default zval */ + } + + efree(fci.params); + memset(&fci, 0, sizeof(fci)); + fci.size = sizeof(fci); + + fci.function_name = pcobj->callable; + args[0] = &retval; + args[1] = &arg; + zend_fcall_info_argp(&fci TSRMLS_CC, 2, args); + fci.retval_ptr_ptr = &newretval; + + if (zend_call_function(&fci, NULL TSRMLS_CC) == FAILURE || EG(exception)) { + efree(fci.params); + zval_ptr_dtor(&retval); + return; + } + + efree(fci.params); + zval_ptr_dtor(&retval); + + RETVAL_ZVAL(newretval, 1 ,1); +} + +PHP_MINIT_FUNCTION(pimple) +{ + zend_class_entry tmp_ce_PsrContainerInterface, tmp_ce_PsrContainerExceptionInterface, tmp_ce_PsrNotFoundExceptionInterface; + zend_class_entry tmp_ce_ExpectedInvokableException, tmp_ce_FrozenServiceException, tmp_ce_InvalidServiceIdentifierException, tmp_ce_UnknownIdentifierException; + zend_class_entry tmp_pimple_ce, tmp_pimple_closure_ce, tmp_pimple_serviceprovider_iface_ce; + + /* Psr\Container namespace */ + INIT_NS_CLASS_ENTRY(tmp_ce_PsrContainerInterface, PSR_CONTAINER_NS, "ContainerInterface", pimple_ce_PsrContainerInterface_functions); + INIT_NS_CLASS_ENTRY(tmp_ce_PsrContainerExceptionInterface, PSR_CONTAINER_NS, "ContainerExceptionInterface", pimple_ce_PsrContainerExceptionInterface_functions); + INIT_NS_CLASS_ENTRY(tmp_ce_PsrNotFoundExceptionInterface, PSR_CONTAINER_NS, "NotFoundExceptionInterface", pimple_ce_PsrNotFoundExceptionInterface_functions); + + pimple_ce_PsrContainerInterface = zend_register_internal_interface(&tmp_ce_PsrContainerInterface TSRMLS_CC); + pimple_ce_PsrContainerExceptionInterface = zend_register_internal_interface(&tmp_ce_PsrContainerExceptionInterface TSRMLS_CC); + pimple_ce_PsrNotFoundExceptionInterface = zend_register_internal_interface(&tmp_ce_PsrNotFoundExceptionInterface TSRMLS_CC); + + zend_class_implements(pimple_ce_PsrNotFoundExceptionInterface TSRMLS_CC, 1, pimple_ce_PsrContainerExceptionInterface); + + /* Pimple\Exception namespace */ + INIT_NS_CLASS_ENTRY(tmp_ce_ExpectedInvokableException, PIMPLE_EXCEPTION_NS, "ExpectedInvokableException", NULL); + INIT_NS_CLASS_ENTRY(tmp_ce_FrozenServiceException, PIMPLE_EXCEPTION_NS, "FrozenServiceException", pimple_ce_FrozenServiceException_functions); + INIT_NS_CLASS_ENTRY(tmp_ce_InvalidServiceIdentifierException, PIMPLE_EXCEPTION_NS, "InvalidServiceIdentifierException", pimple_ce_InvalidServiceIdentifierException_functions); + INIT_NS_CLASS_ENTRY(tmp_ce_UnknownIdentifierException, PIMPLE_EXCEPTION_NS, "UnknownIdentifierException", pimple_ce_UnknownIdentifierException_functions); + + pimple_ce_ExpectedInvokableException = zend_register_internal_class_ex(&tmp_ce_ExpectedInvokableException, spl_ce_InvalidArgumentException, NULL TSRMLS_CC); + pimple_ce_FrozenServiceException = zend_register_internal_class_ex(&tmp_ce_FrozenServiceException, spl_ce_RuntimeException, NULL TSRMLS_CC); + pimple_ce_InvalidServiceIdentifierException = zend_register_internal_class_ex(&tmp_ce_InvalidServiceIdentifierException, spl_ce_InvalidArgumentException, NULL TSRMLS_CC); + pimple_ce_UnknownIdentifierException = zend_register_internal_class_ex(&tmp_ce_UnknownIdentifierException, spl_ce_InvalidArgumentException, NULL TSRMLS_CC); + + zend_class_implements(pimple_ce_ExpectedInvokableException TSRMLS_CC, 1, pimple_ce_PsrContainerExceptionInterface); + zend_class_implements(pimple_ce_FrozenServiceException TSRMLS_CC, 1, pimple_ce_PsrContainerExceptionInterface); + zend_class_implements(pimple_ce_InvalidServiceIdentifierException TSRMLS_CC, 1, pimple_ce_PsrContainerExceptionInterface); + zend_class_implements(pimple_ce_UnknownIdentifierException TSRMLS_CC, 1, pimple_ce_PsrNotFoundExceptionInterface); + + /* Pimple namespace */ + INIT_NS_CLASS_ENTRY(tmp_pimple_ce, PIMPLE_NS, "Container", pimple_ce_functions); + INIT_NS_CLASS_ENTRY(tmp_pimple_closure_ce, PIMPLE_NS, "ContainerClosure", NULL); + INIT_NS_CLASS_ENTRY(tmp_pimple_serviceprovider_iface_ce, PIMPLE_NS, "ServiceProviderInterface", pimple_serviceprovider_iface_ce_functions); + + tmp_pimple_ce.create_object = pimple_object_create; + tmp_pimple_closure_ce.create_object = pimple_closure_object_create; + + pimple_ce = zend_register_internal_class(&tmp_pimple_ce TSRMLS_CC); + zend_class_implements(pimple_ce TSRMLS_CC, 1, zend_ce_arrayaccess); + + pimple_closure_ce = zend_register_internal_class(&tmp_pimple_closure_ce TSRMLS_CC); + pimple_closure_ce->ce_flags |= ZEND_ACC_FINAL_CLASS; + + pimple_serviceprovider_ce = zend_register_internal_interface(&tmp_pimple_serviceprovider_iface_ce TSRMLS_CC); + + memcpy(&pimple_closure_object_handlers, zend_get_std_object_handlers(), sizeof(*zend_get_std_object_handlers())); + pimple_object_handlers = std_object_handlers; + pimple_closure_object_handlers.get_closure = pimple_closure_get_closure; + + pimple_closure_invoker_function.function_name = "Pimple closure internal invoker"; + pimple_closure_invoker_function.fn_flags |= ZEND_ACC_CLOSURE; + pimple_closure_invoker_function.handler = ZEND_MN(PimpleClosure_invoker); + pimple_closure_invoker_function.num_args = 1; + pimple_closure_invoker_function.required_num_args = 1; + pimple_closure_invoker_function.scope = pimple_closure_ce; + pimple_closure_invoker_function.type = ZEND_INTERNAL_FUNCTION; + pimple_closure_invoker_function.module = &pimple_module_entry; + + return SUCCESS; +} + +PHP_MINFO_FUNCTION(pimple) +{ + php_info_print_table_start(); + php_info_print_table_header(2, "SensioLabs Pimple C support", "enabled"); + php_info_print_table_row(2, "Pimple supported version", PIMPLE_VERSION); + php_info_print_table_end(); + + php_info_print_box_start(0); + php_write((void *)ZEND_STRL("SensioLabs Pimple C support developed by Julien Pauli") TSRMLS_CC); + if (!sapi_module.phpinfo_as_text) { + php_write((void *)ZEND_STRL(sensiolabs_logo) TSRMLS_CC); + } + php_info_print_box_end(); +} + +zend_module_entry pimple_module_entry = { + STANDARD_MODULE_HEADER, + "pimple", + NULL, + PHP_MINIT(pimple), + NULL, + NULL, + NULL, + PHP_MINFO(pimple), + PIMPLE_VERSION, + STANDARD_MODULE_PROPERTIES +}; + +#ifdef COMPILE_DL_PIMPLE +ZEND_GET_MODULE(pimple) +#endif diff --git a/lib/silex/vendor/pimple/pimple/ext/pimple/pimple_compat.h b/lib/silex/vendor/pimple/pimple/ext/pimple/pimple_compat.h new file mode 100644 index 000000000..d234e174d --- /dev/null +++ b/lib/silex/vendor/pimple/pimple/ext/pimple/pimple_compat.h @@ -0,0 +1,81 @@ + +/* + * This file is part of Pimple. + * + * Copyright (c) 2014 Fabien Potencier + * + * 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. + */ + +#ifndef PIMPLE_COMPAT_H_ +#define PIMPLE_COMPAT_H_ + +#include "Zend/zend_extensions.h" /* for ZEND_EXTENSION_API_NO */ + +#define PHP_5_0_X_API_NO 220040412 +#define PHP_5_1_X_API_NO 220051025 +#define PHP_5_2_X_API_NO 220060519 +#define PHP_5_3_X_API_NO 220090626 +#define PHP_5_4_X_API_NO 220100525 +#define PHP_5_5_X_API_NO 220121212 +#define PHP_5_6_X_API_NO 220131226 + +#define IS_PHP_56 ZEND_EXTENSION_API_NO == PHP_5_6_X_API_NO +#define IS_AT_LEAST_PHP_56 ZEND_EXTENSION_API_NO >= PHP_5_6_X_API_NO + +#define IS_PHP_55 ZEND_EXTENSION_API_NO == PHP_5_5_X_API_NO +#define IS_AT_LEAST_PHP_55 ZEND_EXTENSION_API_NO >= PHP_5_5_X_API_NO + +#define IS_PHP_54 ZEND_EXTENSION_API_NO == PHP_5_4_X_API_NO +#define IS_AT_LEAST_PHP_54 ZEND_EXTENSION_API_NO >= PHP_5_4_X_API_NO + +#define IS_PHP_53 ZEND_EXTENSION_API_NO == PHP_5_3_X_API_NO +#define IS_AT_LEAST_PHP_53 ZEND_EXTENSION_API_NO >= PHP_5_3_X_API_NO + +#if IS_PHP_53 +#define object_properties_init(obj, ce) do { \ + zend_hash_copy(obj->properties, &ce->default_properties, zval_copy_property_ctor(ce), NULL, sizeof(zval *)); \ + } while (0); +#endif + +#define ZEND_OBJ_INIT(obj, ce) do { \ + zend_object_std_init(obj, ce TSRMLS_CC); \ + object_properties_init((obj), (ce)); \ + } while(0); + +#if IS_PHP_53 || IS_PHP_54 +static void zend_hash_get_current_key_zval_ex(const HashTable *ht, zval *key, HashPosition *pos) { + Bucket *p; + + p = pos ? (*pos) : ht->pInternalPointer; + + if (!p) { + Z_TYPE_P(key) = IS_NULL; + } else if (p->nKeyLength) { + Z_TYPE_P(key) = IS_STRING; + Z_STRVAL_P(key) = estrndup(p->arKey, p->nKeyLength - 1); + Z_STRLEN_P(key) = p->nKeyLength - 1; + } else { + Z_TYPE_P(key) = IS_LONG; + Z_LVAL_P(key) = p->h; + } +} +#endif + +#endif /* PIMPLE_COMPAT_H_ */ diff --git a/lib/silex/vendor/pimple/pimple/ext/pimple/tests/001.phpt b/lib/silex/vendor/pimple/pimple/ext/pimple/tests/001.phpt new file mode 100644 index 000000000..0809ea232 --- /dev/null +++ b/lib/silex/vendor/pimple/pimple/ext/pimple/tests/001.phpt @@ -0,0 +1,45 @@ +--TEST-- +Test for read_dim/write_dim handlers +--SKIPIF-- + +--FILE-- + + +--EXPECTF-- +foo +42 +foo2 +foo99 +baz +strstr \ No newline at end of file diff --git a/lib/silex/vendor/pimple/pimple/ext/pimple/tests/002.phpt b/lib/silex/vendor/pimple/pimple/ext/pimple/tests/002.phpt new file mode 100644 index 000000000..7b56d2c1f --- /dev/null +++ b/lib/silex/vendor/pimple/pimple/ext/pimple/tests/002.phpt @@ -0,0 +1,15 @@ +--TEST-- +Test for constructor +--SKIPIF-- + +--FILE-- +'foo')); +var_dump($p[42]); +?> +--EXPECT-- +NULL +string(3) "foo" diff --git a/lib/silex/vendor/pimple/pimple/ext/pimple/tests/003.phpt b/lib/silex/vendor/pimple/pimple/ext/pimple/tests/003.phpt new file mode 100644 index 000000000..a22cfa352 --- /dev/null +++ b/lib/silex/vendor/pimple/pimple/ext/pimple/tests/003.phpt @@ -0,0 +1,16 @@ +--TEST-- +Test empty dimensions +--SKIPIF-- + +--FILE-- + +--EXPECT-- +int(42) +string(3) "bar" \ No newline at end of file diff --git a/lib/silex/vendor/pimple/pimple/ext/pimple/tests/004.phpt b/lib/silex/vendor/pimple/pimple/ext/pimple/tests/004.phpt new file mode 100644 index 000000000..1e1d25136 --- /dev/null +++ b/lib/silex/vendor/pimple/pimple/ext/pimple/tests/004.phpt @@ -0,0 +1,30 @@ +--TEST-- +Test has/unset dim handlers +--SKIPIF-- + +--FILE-- + +--EXPECT-- +int(42) +NULL +bool(true) +bool(false) +bool(true) +bool(true) \ No newline at end of file diff --git a/lib/silex/vendor/pimple/pimple/ext/pimple/tests/005.phpt b/lib/silex/vendor/pimple/pimple/ext/pimple/tests/005.phpt new file mode 100644 index 000000000..0479ee055 --- /dev/null +++ b/lib/silex/vendor/pimple/pimple/ext/pimple/tests/005.phpt @@ -0,0 +1,27 @@ +--TEST-- +Test simple class inheritance +--SKIPIF-- + +--FILE-- +someAttr; +?> +--EXPECT-- +string(3) "hit" +foo +fooAttr \ No newline at end of file diff --git a/lib/silex/vendor/pimple/pimple/ext/pimple/tests/006.phpt b/lib/silex/vendor/pimple/pimple/ext/pimple/tests/006.phpt new file mode 100644 index 000000000..cfe8a119e --- /dev/null +++ b/lib/silex/vendor/pimple/pimple/ext/pimple/tests/006.phpt @@ -0,0 +1,51 @@ +--TEST-- +Test complex class inheritance +--SKIPIF-- + +--FILE-- + 'bar', 88 => 'baz'); + +$p = new TestPimple($defaultValues); +$p[42] = 'foo'; +var_dump($p[42]); +var_dump($p[0]); +?> +--EXPECT-- +string(13) "hit offsetset" +string(27) "hit offsetget in TestPimple" +string(25) "hit offsetget in MyPimple" +string(3) "foo" +string(27) "hit offsetget in TestPimple" +string(25) "hit offsetget in MyPimple" +string(3) "baz" \ No newline at end of file diff --git a/lib/silex/vendor/pimple/pimple/ext/pimple/tests/007.phpt b/lib/silex/vendor/pimple/pimple/ext/pimple/tests/007.phpt new file mode 100644 index 000000000..5aac68380 --- /dev/null +++ b/lib/silex/vendor/pimple/pimple/ext/pimple/tests/007.phpt @@ -0,0 +1,22 @@ +--TEST-- +Test for read_dim/write_dim handlers +--SKIPIF-- + +--FILE-- + +--EXPECTF-- +foo +42 \ No newline at end of file diff --git a/lib/silex/vendor/pimple/pimple/ext/pimple/tests/008.phpt b/lib/silex/vendor/pimple/pimple/ext/pimple/tests/008.phpt new file mode 100644 index 000000000..db7eeec4a --- /dev/null +++ b/lib/silex/vendor/pimple/pimple/ext/pimple/tests/008.phpt @@ -0,0 +1,29 @@ +--TEST-- +Test frozen services +--SKIPIF-- + +--FILE-- + +--EXPECTF-- diff --git a/lib/silex/vendor/pimple/pimple/ext/pimple/tests/009.phpt b/lib/silex/vendor/pimple/pimple/ext/pimple/tests/009.phpt new file mode 100644 index 000000000..bb05ea296 --- /dev/null +++ b/lib/silex/vendor/pimple/pimple/ext/pimple/tests/009.phpt @@ -0,0 +1,13 @@ +--TEST-- +Test service is called as callback, and only once +--SKIPIF-- + +--FILE-- + +--EXPECTF-- +bool(true) \ No newline at end of file diff --git a/lib/silex/vendor/pimple/pimple/ext/pimple/tests/010.phpt b/lib/silex/vendor/pimple/pimple/ext/pimple/tests/010.phpt new file mode 100644 index 000000000..badce0146 --- /dev/null +++ b/lib/silex/vendor/pimple/pimple/ext/pimple/tests/010.phpt @@ -0,0 +1,45 @@ +--TEST-- +Test service is called as callback for every callback type +--SKIPIF-- + +--FILE-- + +--EXPECTF-- +callme +called +Foo::bar +array(2) { + [0]=> + string(3) "Foo" + [1]=> + string(3) "bar" +} \ No newline at end of file diff --git a/lib/silex/vendor/pimple/pimple/ext/pimple/tests/011.phpt b/lib/silex/vendor/pimple/pimple/ext/pimple/tests/011.phpt new file mode 100644 index 000000000..6682ab8eb --- /dev/null +++ b/lib/silex/vendor/pimple/pimple/ext/pimple/tests/011.phpt @@ -0,0 +1,19 @@ +--TEST-- +Test service callback throwing an exception +--SKIPIF-- + +--FILE-- + +--EXPECTF-- +all right! \ No newline at end of file diff --git a/lib/silex/vendor/pimple/pimple/ext/pimple/tests/012.phpt b/lib/silex/vendor/pimple/pimple/ext/pimple/tests/012.phpt new file mode 100644 index 000000000..4c6ac486d --- /dev/null +++ b/lib/silex/vendor/pimple/pimple/ext/pimple/tests/012.phpt @@ -0,0 +1,28 @@ +--TEST-- +Test service factory +--SKIPIF-- + +--FILE-- +factory($f = function() { var_dump('called-1'); return 'ret-1';}); + +$p[] = $f; + +$p[] = function () { var_dump('called-2'); return 'ret-2'; }; + +var_dump($p[0]); +var_dump($p[0]); +var_dump($p[1]); +var_dump($p[1]); +?> +--EXPECTF-- +string(8) "called-1" +string(5) "ret-1" +string(8) "called-1" +string(5) "ret-1" +string(8) "called-2" +string(5) "ret-2" +string(5) "ret-2" \ No newline at end of file diff --git a/lib/silex/vendor/pimple/pimple/ext/pimple/tests/013.phpt b/lib/silex/vendor/pimple/pimple/ext/pimple/tests/013.phpt new file mode 100644 index 000000000..f419958c5 --- /dev/null +++ b/lib/silex/vendor/pimple/pimple/ext/pimple/tests/013.phpt @@ -0,0 +1,33 @@ +--TEST-- +Test keys() +--SKIPIF-- + +--FILE-- +keys()); + +$p['foo'] = 'bar'; +$p[] = 'foo'; + +var_dump($p->keys()); + +unset($p['foo']); + +var_dump($p->keys()); +?> +--EXPECTF-- +array(0) { +} +array(2) { + [0]=> + string(3) "foo" + [1]=> + int(0) +} +array(1) { + [0]=> + int(0) +} \ No newline at end of file diff --git a/lib/silex/vendor/pimple/pimple/ext/pimple/tests/014.phpt b/lib/silex/vendor/pimple/pimple/ext/pimple/tests/014.phpt new file mode 100644 index 000000000..ac937213a --- /dev/null +++ b/lib/silex/vendor/pimple/pimple/ext/pimple/tests/014.phpt @@ -0,0 +1,30 @@ +--TEST-- +Test raw() +--SKIPIF-- + +--FILE-- +raw('foo')); +var_dump($p[42]); + +unset($p['foo']); + +try { + $p->raw('foo'); + echo "expected exception"; +} catch (InvalidArgumentException $e) { } +--EXPECTF-- +string(8) "called-2" +string(5) "ret-2" +object(Closure)#%i (0) { +} +string(8) "called-2" +string(5) "ret-2" \ No newline at end of file diff --git a/lib/silex/vendor/pimple/pimple/ext/pimple/tests/015.phpt b/lib/silex/vendor/pimple/pimple/ext/pimple/tests/015.phpt new file mode 100644 index 000000000..314f008ac --- /dev/null +++ b/lib/silex/vendor/pimple/pimple/ext/pimple/tests/015.phpt @@ -0,0 +1,17 @@ +--TEST-- +Test protect() +--SKIPIF-- + +--FILE-- +protect($f); + +var_dump($p['foo']); +--EXPECTF-- +object(Closure)#%i (0) { +} \ No newline at end of file diff --git a/lib/silex/vendor/pimple/pimple/ext/pimple/tests/016.phpt b/lib/silex/vendor/pimple/pimple/ext/pimple/tests/016.phpt new file mode 100644 index 000000000..e55edb0a7 --- /dev/null +++ b/lib/silex/vendor/pimple/pimple/ext/pimple/tests/016.phpt @@ -0,0 +1,24 @@ +--TEST-- +Test extend() +--SKIPIF-- + +--FILE-- +extend(12, function ($w) { var_dump($w); return 'bar'; }); /* $callable in code above */ + +var_dump($c('param')); +--EXPECTF-- +string(5) "param" +string(3) "foo" +string(3) "bar" \ No newline at end of file diff --git a/lib/silex/vendor/pimple/pimple/ext/pimple/tests/017.phpt b/lib/silex/vendor/pimple/pimple/ext/pimple/tests/017.phpt new file mode 100644 index 000000000..bac23ce09 --- /dev/null +++ b/lib/silex/vendor/pimple/pimple/ext/pimple/tests/017.phpt @@ -0,0 +1,17 @@ +--TEST-- +Test extend() with exception in service extension +--SKIPIF-- + +--FILE-- +extend(12, function ($w) { throw new BadMethodCallException; }); + +try { + $p[12]; + echo "Exception expected"; +} catch (BadMethodCallException $e) { } +--EXPECTF-- diff --git a/lib/silex/vendor/pimple/pimple/ext/pimple/tests/017_1.phpt b/lib/silex/vendor/pimple/pimple/ext/pimple/tests/017_1.phpt new file mode 100644 index 000000000..8f881d6eb --- /dev/null +++ b/lib/silex/vendor/pimple/pimple/ext/pimple/tests/017_1.phpt @@ -0,0 +1,17 @@ +--TEST-- +Test extend() with exception in service factory +--SKIPIF-- + +--FILE-- +extend(12, function ($w) { return 'foobar'; }); + +try { + $p[12]; + echo "Exception expected"; +} catch (BadMethodCallException $e) { } +--EXPECTF-- diff --git a/lib/silex/vendor/pimple/pimple/ext/pimple/tests/018.phpt b/lib/silex/vendor/pimple/pimple/ext/pimple/tests/018.phpt new file mode 100644 index 000000000..27c12a14e --- /dev/null +++ b/lib/silex/vendor/pimple/pimple/ext/pimple/tests/018.phpt @@ -0,0 +1,23 @@ +--TEST-- +Test register() +--SKIPIF-- + +--FILE-- +register(new Foo, array(42 => 'bar')); + +var_dump($p[42]); +--EXPECTF-- +object(Pimple\Container)#1 (0) { +} +string(3) "bar" \ No newline at end of file diff --git a/lib/silex/vendor/pimple/pimple/ext/pimple/tests/019.phpt b/lib/silex/vendor/pimple/pimple/ext/pimple/tests/019.phpt new file mode 100644 index 000000000..28a9aecac --- /dev/null +++ b/lib/silex/vendor/pimple/pimple/ext/pimple/tests/019.phpt @@ -0,0 +1,18 @@ +--TEST-- +Test register() returns static and is a fluent interface +--SKIPIF-- + +--FILE-- +register(new Foo)); +--EXPECTF-- +bool(true) diff --git a/lib/silex/vendor/pimple/pimple/ext/pimple/tests/bench.phpb b/lib/silex/vendor/pimple/pimple/ext/pimple/tests/bench.phpb new file mode 100644 index 000000000..8f983e656 --- /dev/null +++ b/lib/silex/vendor/pimple/pimple/ext/pimple/tests/bench.phpb @@ -0,0 +1,51 @@ +factory($factory); + +$p['factory'] = $factory; + +echo $p['factory']; +echo $p['factory']; +echo $p['factory']; + +} + +echo microtime(true) - $time; diff --git a/lib/silex/vendor/pimple/pimple/ext/pimple/tests/bench_shared.phpb b/lib/silex/vendor/pimple/pimple/ext/pimple/tests/bench_shared.phpb new file mode 100644 index 000000000..aec541f0b --- /dev/null +++ b/lib/silex/vendor/pimple/pimple/ext/pimple/tests/bench_shared.phpb @@ -0,0 +1,25 @@ + diff --git a/lib/silex/vendor/pimple/pimple/phpunit.xml.dist b/lib/silex/vendor/pimple/pimple/phpunit.xml.dist new file mode 100644 index 000000000..5c8d487fe --- /dev/null +++ b/lib/silex/vendor/pimple/pimple/phpunit.xml.dist @@ -0,0 +1,14 @@ + + + + + + ./src/Pimple/Tests + + + diff --git a/lib/silex/vendor/pimple/pimple/src/Pimple/Container.php b/lib/silex/vendor/pimple/pimple/src/Pimple/Container.php new file mode 100644 index 000000000..707b92b82 --- /dev/null +++ b/lib/silex/vendor/pimple/pimple/src/Pimple/Container.php @@ -0,0 +1,298 @@ +factories = new \SplObjectStorage(); + $this->protected = new \SplObjectStorage(); + + foreach ($values as $key => $value) { + $this->offsetSet($key, $value); + } + } + + /** + * Sets a parameter or an object. + * + * Objects must be defined as Closures. + * + * Allowing any PHP callable leads to difficult to debug problems + * as function names (strings) are callable (creating a function with + * the same name as an existing parameter would break your container). + * + * @param string $id The unique identifier for the parameter or object + * @param mixed $value The value of the parameter or a closure to define an object + * + * @throws FrozenServiceException Prevent override of a frozen service + */ + public function offsetSet($id, $value) + { + if (isset($this->frozen[$id])) { + throw new FrozenServiceException($id); + } + + $this->values[$id] = $value; + $this->keys[$id] = true; + } + + /** + * Gets a parameter or an object. + * + * @param string $id The unique identifier for the parameter or object + * + * @return mixed The value of the parameter or an object + * + * @throws UnknownIdentifierException If the identifier is not defined + */ + public function offsetGet($id) + { + if (!isset($this->keys[$id])) { + throw new UnknownIdentifierException($id); + } + + if ( + isset($this->raw[$id]) + || !\is_object($this->values[$id]) + || isset($this->protected[$this->values[$id]]) + || !\method_exists($this->values[$id], '__invoke') + ) { + return $this->values[$id]; + } + + if (isset($this->factories[$this->values[$id]])) { + return $this->values[$id]($this); + } + + $raw = $this->values[$id]; + $val = $this->values[$id] = $raw($this); + $this->raw[$id] = $raw; + + $this->frozen[$id] = true; + + return $val; + } + + /** + * Checks if a parameter or an object is set. + * + * @param string $id The unique identifier for the parameter or object + * + * @return bool + */ + public function offsetExists($id) + { + return isset($this->keys[$id]); + } + + /** + * Unsets a parameter or an object. + * + * @param string $id The unique identifier for the parameter or object + */ + public function offsetUnset($id) + { + if (isset($this->keys[$id])) { + if (\is_object($this->values[$id])) { + unset($this->factories[$this->values[$id]], $this->protected[$this->values[$id]]); + } + + unset($this->values[$id], $this->frozen[$id], $this->raw[$id], $this->keys[$id]); + } + } + + /** + * Marks a callable as being a factory service. + * + * @param callable $callable A service definition to be used as a factory + * + * @return callable The passed callable + * + * @throws ExpectedInvokableException Service definition has to be a closure or an invokable object + */ + public function factory($callable) + { + if (!\method_exists($callable, '__invoke')) { + throw new ExpectedInvokableException('Service definition is not a Closure or invokable object.'); + } + + $this->factories->attach($callable); + + return $callable; + } + + /** + * Protects a callable from being interpreted as a service. + * + * This is useful when you want to store a callable as a parameter. + * + * @param callable $callable A callable to protect from being evaluated + * + * @return callable The passed callable + * + * @throws ExpectedInvokableException Service definition has to be a closure or an invokable object + */ + public function protect($callable) + { + if (!\method_exists($callable, '__invoke')) { + throw new ExpectedInvokableException('Callable is not a Closure or invokable object.'); + } + + $this->protected->attach($callable); + + return $callable; + } + + /** + * Gets a parameter or the closure defining an object. + * + * @param string $id The unique identifier for the parameter or object + * + * @return mixed The value of the parameter or the closure defining an object + * + * @throws UnknownIdentifierException If the identifier is not defined + */ + public function raw($id) + { + if (!isset($this->keys[$id])) { + throw new UnknownIdentifierException($id); + } + + if (isset($this->raw[$id])) { + return $this->raw[$id]; + } + + return $this->values[$id]; + } + + /** + * Extends an object definition. + * + * Useful when you want to extend an existing object definition, + * without necessarily loading that object. + * + * @param string $id The unique identifier for the object + * @param callable $callable A service definition to extend the original + * + * @return callable The wrapped callable + * + * @throws UnknownIdentifierException If the identifier is not defined + * @throws FrozenServiceException If the service is frozen + * @throws InvalidServiceIdentifierException If the identifier belongs to a parameter + * @throws ExpectedInvokableException If the extension callable is not a closure or an invokable object + */ + public function extend($id, $callable) + { + if (!isset($this->keys[$id])) { + throw new UnknownIdentifierException($id); + } + + if (isset($this->frozen[$id])) { + throw new FrozenServiceException($id); + } + + if (!\is_object($this->values[$id]) || !\method_exists($this->values[$id], '__invoke')) { + throw new InvalidServiceIdentifierException($id); + } + + if (isset($this->protected[$this->values[$id]])) { + @\trigger_error(\sprintf('How Pimple behaves when extending protected closures will be fixed in Pimple 4. Are you sure "%s" should be protected?', $id), \E_USER_DEPRECATED); + } + + if (!\is_object($callable) || !\method_exists($callable, '__invoke')) { + throw new ExpectedInvokableException('Extension service definition is not a Closure or invokable object.'); + } + + $factory = $this->values[$id]; + + $extended = function ($c) use ($callable, $factory) { + return $callable($factory($c), $c); + }; + + if (isset($this->factories[$factory])) { + $this->factories->detach($factory); + $this->factories->attach($extended); + } + + return $this[$id] = $extended; + } + + /** + * Returns all defined value names. + * + * @return array An array of value names + */ + public function keys() + { + return \array_keys($this->values); + } + + /** + * Registers a service provider. + * + * @param ServiceProviderInterface $provider A ServiceProviderInterface instance + * @param array $values An array of values that customizes the provider + * + * @return static + */ + public function register(ServiceProviderInterface $provider, array $values = array()) + { + $provider->register($this); + + foreach ($values as $key => $value) { + $this[$key] = $value; + } + + return $this; + } +} diff --git a/lib/silex/vendor/pimple/pimple/src/Pimple/Exception/ExpectedInvokableException.php b/lib/silex/vendor/pimple/pimple/src/Pimple/Exception/ExpectedInvokableException.php new file mode 100644 index 000000000..7228421b1 --- /dev/null +++ b/lib/silex/vendor/pimple/pimple/src/Pimple/Exception/ExpectedInvokableException.php @@ -0,0 +1,38 @@ + + */ +class ExpectedInvokableException extends \InvalidArgumentException implements ContainerExceptionInterface +{ +} diff --git a/lib/silex/vendor/pimple/pimple/src/Pimple/Exception/FrozenServiceException.php b/lib/silex/vendor/pimple/pimple/src/Pimple/Exception/FrozenServiceException.php new file mode 100644 index 000000000..e4d2f6d36 --- /dev/null +++ b/lib/silex/vendor/pimple/pimple/src/Pimple/Exception/FrozenServiceException.php @@ -0,0 +1,45 @@ + + */ +class FrozenServiceException extends \RuntimeException implements ContainerExceptionInterface +{ + /** + * @param string $id Identifier of the frozen service + */ + public function __construct($id) + { + parent::__construct(\sprintf('Cannot override frozen service "%s".', $id)); + } +} diff --git a/lib/silex/vendor/pimple/pimple/src/Pimple/Exception/InvalidServiceIdentifierException.php b/lib/silex/vendor/pimple/pimple/src/Pimple/Exception/InvalidServiceIdentifierException.php new file mode 100644 index 000000000..91e82f98c --- /dev/null +++ b/lib/silex/vendor/pimple/pimple/src/Pimple/Exception/InvalidServiceIdentifierException.php @@ -0,0 +1,45 @@ + + */ +class InvalidServiceIdentifierException extends \InvalidArgumentException implements NotFoundExceptionInterface +{ + /** + * @param string $id The invalid identifier + */ + public function __construct($id) + { + parent::__construct(\sprintf('Identifier "%s" does not contain an object definition.', $id)); + } +} diff --git a/lib/silex/vendor/pimple/pimple/src/Pimple/Exception/UnknownIdentifierException.php b/lib/silex/vendor/pimple/pimple/src/Pimple/Exception/UnknownIdentifierException.php new file mode 100644 index 000000000..fb6b626e2 --- /dev/null +++ b/lib/silex/vendor/pimple/pimple/src/Pimple/Exception/UnknownIdentifierException.php @@ -0,0 +1,45 @@ + + */ +class UnknownIdentifierException extends \InvalidArgumentException implements NotFoundExceptionInterface +{ + /** + * @param string $id The unknown identifier + */ + public function __construct($id) + { + parent::__construct(\sprintf('Identifier "%s" is not defined.', $id)); + } +} diff --git a/lib/silex/vendor/pimple/pimple/src/Pimple/Psr11/Container.php b/lib/silex/vendor/pimple/pimple/src/Pimple/Psr11/Container.php new file mode 100644 index 000000000..cadbfffad --- /dev/null +++ b/lib/silex/vendor/pimple/pimple/src/Pimple/Psr11/Container.php @@ -0,0 +1,55 @@ + + */ +final class Container implements ContainerInterface +{ + private $pimple; + + public function __construct(PimpleContainer $pimple) + { + $this->pimple = $pimple; + } + + public function get($id) + { + return $this->pimple[$id]; + } + + public function has($id) + { + return isset($this->pimple[$id]); + } +} diff --git a/lib/silex/vendor/pimple/pimple/src/Pimple/Psr11/ServiceLocator.php b/lib/silex/vendor/pimple/pimple/src/Pimple/Psr11/ServiceLocator.php new file mode 100644 index 000000000..3361c6f19 --- /dev/null +++ b/lib/silex/vendor/pimple/pimple/src/Pimple/Psr11/ServiceLocator.php @@ -0,0 +1,75 @@ + + */ +class ServiceLocator implements ContainerInterface +{ + private $container; + private $aliases = array(); + + /** + * @param PimpleContainer $container The Container instance used to locate services + * @param array $ids Array of service ids that can be located. String keys can be used to define aliases + */ + public function __construct(PimpleContainer $container, array $ids) + { + $this->container = $container; + + foreach ($ids as $key => $id) { + $this->aliases[\is_int($key) ? $id : $key] = $id; + } + } + + /** + * {@inheritdoc} + */ + public function get($id) + { + if (!isset($this->aliases[$id])) { + throw new UnknownIdentifierException($id); + } + + return $this->container[$this->aliases[$id]]; + } + + /** + * {@inheritdoc} + */ + public function has($id) + { + return isset($this->aliases[$id]) && isset($this->container[$this->aliases[$id]]); + } +} diff --git a/lib/silex/vendor/pimple/pimple/src/Pimple/ServiceIterator.php b/lib/silex/vendor/pimple/pimple/src/Pimple/ServiceIterator.php new file mode 100644 index 000000000..5cde5188f --- /dev/null +++ b/lib/silex/vendor/pimple/pimple/src/Pimple/ServiceIterator.php @@ -0,0 +1,69 @@ + + */ +final class ServiceIterator implements \Iterator +{ + private $container; + private $ids; + + public function __construct(Container $container, array $ids) + { + $this->container = $container; + $this->ids = $ids; + } + + public function rewind() + { + \reset($this->ids); + } + + public function current() + { + return $this->container[\current($this->ids)]; + } + + public function key() + { + return \current($this->ids); + } + + public function next() + { + \next($this->ids); + } + + public function valid() + { + return null !== \key($this->ids); + } +} diff --git a/lib/silex/vendor/pimple/pimple/src/Pimple/ServiceProviderInterface.php b/lib/silex/vendor/pimple/pimple/src/Pimple/ServiceProviderInterface.php new file mode 100644 index 000000000..c004594ba --- /dev/null +++ b/lib/silex/vendor/pimple/pimple/src/Pimple/ServiceProviderInterface.php @@ -0,0 +1,46 @@ +value = $value; + + return $service; + } +} diff --git a/lib/silex/vendor/pimple/pimple/src/Pimple/Tests/Fixtures/NonInvokable.php b/lib/silex/vendor/pimple/pimple/src/Pimple/Tests/Fixtures/NonInvokable.php new file mode 100644 index 000000000..33cd4e548 --- /dev/null +++ b/lib/silex/vendor/pimple/pimple/src/Pimple/Tests/Fixtures/NonInvokable.php @@ -0,0 +1,34 @@ +factory(function () { + return new Service(); + }); + } +} diff --git a/lib/silex/vendor/pimple/pimple/src/Pimple/Tests/Fixtures/Service.php b/lib/silex/vendor/pimple/pimple/src/Pimple/Tests/Fixtures/Service.php new file mode 100644 index 000000000..d71b184dd --- /dev/null +++ b/lib/silex/vendor/pimple/pimple/src/Pimple/Tests/Fixtures/Service.php @@ -0,0 +1,35 @@ + + */ +class Service +{ + public $value; +} diff --git a/lib/silex/vendor/pimple/pimple/src/Pimple/Tests/PimpleServiceProviderInterfaceTest.php b/lib/silex/vendor/pimple/pimple/src/Pimple/Tests/PimpleServiceProviderInterfaceTest.php new file mode 100644 index 000000000..8e5c4c73d --- /dev/null +++ b/lib/silex/vendor/pimple/pimple/src/Pimple/Tests/PimpleServiceProviderInterfaceTest.php @@ -0,0 +1,76 @@ + + */ +class PimpleServiceProviderInterfaceTest extends \PHPUnit_Framework_TestCase +{ + public function testProvider() + { + $pimple = new Container(); + + $pimpleServiceProvider = new Fixtures\PimpleServiceProvider(); + $pimpleServiceProvider->register($pimple); + + $this->assertEquals('value', $pimple['param']); + $this->assertInstanceOf('Pimple\Tests\Fixtures\Service', $pimple['service']); + + $serviceOne = $pimple['factory']; + $this->assertInstanceOf('Pimple\Tests\Fixtures\Service', $serviceOne); + + $serviceTwo = $pimple['factory']; + $this->assertInstanceOf('Pimple\Tests\Fixtures\Service', $serviceTwo); + + $this->assertNotSame($serviceOne, $serviceTwo); + } + + public function testProviderWithRegisterMethod() + { + $pimple = new Container(); + + $pimple->register(new Fixtures\PimpleServiceProvider(), array( + 'anotherParameter' => 'anotherValue', + )); + + $this->assertEquals('value', $pimple['param']); + $this->assertEquals('anotherValue', $pimple['anotherParameter']); + + $this->assertInstanceOf('Pimple\Tests\Fixtures\Service', $pimple['service']); + + $serviceOne = $pimple['factory']; + $this->assertInstanceOf('Pimple\Tests\Fixtures\Service', $serviceOne); + + $serviceTwo = $pimple['factory']; + $this->assertInstanceOf('Pimple\Tests\Fixtures\Service', $serviceTwo); + + $this->assertNotSame($serviceOne, $serviceTwo); + } +} diff --git a/lib/silex/vendor/pimple/pimple/src/Pimple/Tests/PimpleTest.php b/lib/silex/vendor/pimple/pimple/src/Pimple/Tests/PimpleTest.php new file mode 100644 index 000000000..acb66e000 --- /dev/null +++ b/lib/silex/vendor/pimple/pimple/src/Pimple/Tests/PimpleTest.php @@ -0,0 +1,589 @@ + + */ +class PimpleTest extends \PHPUnit_Framework_TestCase +{ + public function testWithString() + { + $pimple = new Container(); + $pimple['param'] = 'value'; + + $this->assertEquals('value', $pimple['param']); + } + + public function testWithClosure() + { + $pimple = new Container(); + $pimple['service'] = function () { + return new Fixtures\Service(); + }; + + $this->assertInstanceOf('Pimple\Tests\Fixtures\Service', $pimple['service']); + } + + public function testServicesShouldBeDifferent() + { + $pimple = new Container(); + $pimple['service'] = $pimple->factory(function () { + return new Fixtures\Service(); + }); + + $serviceOne = $pimple['service']; + $this->assertInstanceOf('Pimple\Tests\Fixtures\Service', $serviceOne); + + $serviceTwo = $pimple['service']; + $this->assertInstanceOf('Pimple\Tests\Fixtures\Service', $serviceTwo); + + $this->assertNotSame($serviceOne, $serviceTwo); + } + + public function testShouldPassContainerAsParameter() + { + $pimple = new Container(); + $pimple['service'] = function () { + return new Fixtures\Service(); + }; + $pimple['container'] = function ($container) { + return $container; + }; + + $this->assertNotSame($pimple, $pimple['service']); + $this->assertSame($pimple, $pimple['container']); + } + + public function testIsset() + { + $pimple = new Container(); + $pimple['param'] = 'value'; + $pimple['service'] = function () { + return new Fixtures\Service(); + }; + + $pimple['null'] = null; + + $this->assertTrue(isset($pimple['param'])); + $this->assertTrue(isset($pimple['service'])); + $this->assertTrue(isset($pimple['null'])); + $this->assertFalse(isset($pimple['non_existent'])); + } + + public function testConstructorInjection() + { + $params = array('param' => 'value'); + $pimple = new Container($params); + + $this->assertSame($params['param'], $pimple['param']); + } + + /** + * @expectedException \Pimple\Exception\UnknownIdentifierException + * @expectedExceptionMessage Identifier "foo" is not defined. + */ + public function testOffsetGetValidatesKeyIsPresent() + { + $pimple = new Container(); + echo $pimple['foo']; + } + + /** + * @group legacy + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage Identifier "foo" is not defined. + */ + public function testLegacyOffsetGetValidatesKeyIsPresent() + { + $pimple = new Container(); + echo $pimple['foo']; + } + + public function testOffsetGetHonorsNullValues() + { + $pimple = new Container(); + $pimple['foo'] = null; + $this->assertNull($pimple['foo']); + } + + public function testUnset() + { + $pimple = new Container(); + $pimple['param'] = 'value'; + $pimple['service'] = function () { + return new Fixtures\Service(); + }; + + unset($pimple['param'], $pimple['service']); + $this->assertFalse(isset($pimple['param'])); + $this->assertFalse(isset($pimple['service'])); + } + + /** + * @dataProvider serviceDefinitionProvider + */ + public function testShare($service) + { + $pimple = new Container(); + $pimple['shared_service'] = $service; + + $serviceOne = $pimple['shared_service']; + $this->assertInstanceOf('Pimple\Tests\Fixtures\Service', $serviceOne); + + $serviceTwo = $pimple['shared_service']; + $this->assertInstanceOf('Pimple\Tests\Fixtures\Service', $serviceTwo); + + $this->assertSame($serviceOne, $serviceTwo); + } + + /** + * @dataProvider serviceDefinitionProvider + */ + public function testProtect($service) + { + $pimple = new Container(); + $pimple['protected'] = $pimple->protect($service); + + $this->assertSame($service, $pimple['protected']); + } + + public function testGlobalFunctionNameAsParameterValue() + { + $pimple = new Container(); + $pimple['global_function'] = 'strlen'; + $this->assertSame('strlen', $pimple['global_function']); + } + + public function testRaw() + { + $pimple = new Container(); + $pimple['service'] = $definition = $pimple->factory(function () { return 'foo'; }); + $this->assertSame($definition, $pimple->raw('service')); + } + + public function testRawHonorsNullValues() + { + $pimple = new Container(); + $pimple['foo'] = null; + $this->assertNull($pimple->raw('foo')); + } + + public function testFluentRegister() + { + $pimple = new Container(); + $this->assertSame($pimple, $pimple->register($this->getMockBuilder('Pimple\ServiceProviderInterface')->getMock())); + } + + /** + * @expectedException \Pimple\Exception\UnknownIdentifierException + * @expectedExceptionMessage Identifier "foo" is not defined. + */ + public function testRawValidatesKeyIsPresent() + { + $pimple = new Container(); + $pimple->raw('foo'); + } + + /** + * @group legacy + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage Identifier "foo" is not defined. + */ + public function testLegacyRawValidatesKeyIsPresent() + { + $pimple = new Container(); + $pimple->raw('foo'); + } + + /** + * @dataProvider serviceDefinitionProvider + */ + public function testExtend($service) + { + $pimple = new Container(); + $pimple['shared_service'] = function () { + return new Fixtures\Service(); + }; + $pimple['factory_service'] = $pimple->factory(function () { + return new Fixtures\Service(); + }); + + $pimple->extend('shared_service', $service); + $serviceOne = $pimple['shared_service']; + $this->assertInstanceOf('Pimple\Tests\Fixtures\Service', $serviceOne); + $serviceTwo = $pimple['shared_service']; + $this->assertInstanceOf('Pimple\Tests\Fixtures\Service', $serviceTwo); + $this->assertSame($serviceOne, $serviceTwo); + $this->assertSame($serviceOne->value, $serviceTwo->value); + + $pimple->extend('factory_service', $service); + $serviceOne = $pimple['factory_service']; + $this->assertInstanceOf('Pimple\Tests\Fixtures\Service', $serviceOne); + $serviceTwo = $pimple['factory_service']; + $this->assertInstanceOf('Pimple\Tests\Fixtures\Service', $serviceTwo); + $this->assertNotSame($serviceOne, $serviceTwo); + $this->assertNotSame($serviceOne->value, $serviceTwo->value); + } + + public function testExtendDoesNotLeakWithFactories() + { + if (extension_loaded('pimple')) { + $this->markTestSkipped('Pimple extension does not support this test'); + } + $pimple = new Container(); + + $pimple['foo'] = $pimple->factory(function () { return; }); + $pimple['foo'] = $pimple->extend('foo', function ($foo, $pimple) { return; }); + unset($pimple['foo']); + + $p = new \ReflectionProperty($pimple, 'values'); + $p->setAccessible(true); + $this->assertEmpty($p->getValue($pimple)); + + $p = new \ReflectionProperty($pimple, 'factories'); + $p->setAccessible(true); + $this->assertCount(0, $p->getValue($pimple)); + } + + /** + * @expectedException \Pimple\Exception\UnknownIdentifierException + * @expectedExceptionMessage Identifier "foo" is not defined. + */ + public function testExtendValidatesKeyIsPresent() + { + $pimple = new Container(); + $pimple->extend('foo', function () {}); + } + + /** + * @group legacy + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage Identifier "foo" is not defined. + */ + public function testLegacyExtendValidatesKeyIsPresent() + { + $pimple = new Container(); + $pimple->extend('foo', function () {}); + } + + public function testKeys() + { + $pimple = new Container(); + $pimple['foo'] = 123; + $pimple['bar'] = 123; + + $this->assertEquals(array('foo', 'bar'), $pimple->keys()); + } + + /** @test */ + public function settingAnInvokableObjectShouldTreatItAsFactory() + { + $pimple = new Container(); + $pimple['invokable'] = new Fixtures\Invokable(); + + $this->assertInstanceOf('Pimple\Tests\Fixtures\Service', $pimple['invokable']); + } + + /** @test */ + public function settingNonInvokableObjectShouldTreatItAsParameter() + { + $pimple = new Container(); + $pimple['non_invokable'] = new Fixtures\NonInvokable(); + + $this->assertInstanceOf('Pimple\Tests\Fixtures\NonInvokable', $pimple['non_invokable']); + } + + /** + * @dataProvider badServiceDefinitionProvider + * @expectedException \Pimple\Exception\ExpectedInvokableException + * @expectedExceptionMessage Service definition is not a Closure or invokable object. + */ + public function testFactoryFailsForInvalidServiceDefinitions($service) + { + $pimple = new Container(); + $pimple->factory($service); + } + + /** + * @group legacy + * @dataProvider badServiceDefinitionProvider + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage Service definition is not a Closure or invokable object. + */ + public function testLegacyFactoryFailsForInvalidServiceDefinitions($service) + { + $pimple = new Container(); + $pimple->factory($service); + } + + /** + * @dataProvider badServiceDefinitionProvider + * @expectedException \Pimple\Exception\ExpectedInvokableException + * @expectedExceptionMessage Callable is not a Closure or invokable object. + */ + public function testProtectFailsForInvalidServiceDefinitions($service) + { + $pimple = new Container(); + $pimple->protect($service); + } + + /** + * @group legacy + * @dataProvider badServiceDefinitionProvider + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage Callable is not a Closure or invokable object. + */ + public function testLegacyProtectFailsForInvalidServiceDefinitions($service) + { + $pimple = new Container(); + $pimple->protect($service); + } + + /** + * @dataProvider badServiceDefinitionProvider + * @expectedException \Pimple\Exception\InvalidServiceIdentifierException + * @expectedExceptionMessage Identifier "foo" does not contain an object definition. + */ + public function testExtendFailsForKeysNotContainingServiceDefinitions($service) + { + $pimple = new Container(); + $pimple['foo'] = $service; + $pimple->extend('foo', function () {}); + } + + /** + * @group legacy + * @dataProvider badServiceDefinitionProvider + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage Identifier "foo" does not contain an object definition. + */ + public function testLegacyExtendFailsForKeysNotContainingServiceDefinitions($service) + { + $pimple = new Container(); + $pimple['foo'] = $service; + $pimple->extend('foo', function () {}); + } + + /** + * @group legacy + * @expectedDeprecation How Pimple behaves when extending protected closures will be fixed in Pimple 4. Are you sure "foo" should be protected? + */ + public function testExtendingProtectedClosureDeprecation() + { + $pimple = new Container(); + $pimple['foo'] = $pimple->protect(function () { + return 'bar'; + }); + + $pimple->extend('foo', function ($value) { + return $value.'-baz'; + }); + + $this->assertSame('bar-baz', $pimple['foo']); + } + + /** + * @dataProvider badServiceDefinitionProvider + * @expectedException \Pimple\Exception\ExpectedInvokableException + * @expectedExceptionMessage Extension service definition is not a Closure or invokable object. + */ + public function testExtendFailsForInvalidServiceDefinitions($service) + { + $pimple = new Container(); + $pimple['foo'] = function () {}; + $pimple->extend('foo', $service); + } + + /** + * @group legacy + * @dataProvider badServiceDefinitionProvider + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage Extension service definition is not a Closure or invokable object. + */ + public function testLegacyExtendFailsForInvalidServiceDefinitions($service) + { + $pimple = new Container(); + $pimple['foo'] = function () {}; + $pimple->extend('foo', $service); + } + + /** + * @expectedException \Pimple\Exception\FrozenServiceException + * @expectedExceptionMessage Cannot override frozen service "foo". + */ + public function testExtendFailsIfFrozenServiceIsNonInvokable() + { + $pimple = new Container(); + $pimple['foo'] = function () { + return new Fixtures\NonInvokable(); + }; + $foo = $pimple['foo']; + + $pimple->extend('foo', function () {}); + } + + /** + * @expectedException \Pimple\Exception\FrozenServiceException + * @expectedExceptionMessage Cannot override frozen service "foo". + */ + public function testExtendFailsIfFrozenServiceIsInvokable() + { + $pimple = new Container(); + $pimple['foo'] = function () { + return new Fixtures\Invokable(); + }; + $foo = $pimple['foo']; + + $pimple->extend('foo', function () {}); + } + + /** + * Provider for invalid service definitions. + */ + public function badServiceDefinitionProvider() + { + return array( + array(123), + array(new Fixtures\NonInvokable()), + ); + } + + /** + * Provider for service definitions. + */ + public function serviceDefinitionProvider() + { + return array( + array(function ($value) { + $service = new Fixtures\Service(); + $service->value = $value; + + return $service; + }), + array(new Fixtures\Invokable()), + ); + } + + public function testDefiningNewServiceAfterFreeze() + { + $pimple = new Container(); + $pimple['foo'] = function () { + return 'foo'; + }; + $foo = $pimple['foo']; + + $pimple['bar'] = function () { + return 'bar'; + }; + $this->assertSame('bar', $pimple['bar']); + } + + /** + * @expectedException \Pimple\Exception\FrozenServiceException + * @expectedExceptionMessage Cannot override frozen service "foo". + */ + public function testOverridingServiceAfterFreeze() + { + $pimple = new Container(); + $pimple['foo'] = function () { + return 'foo'; + }; + $foo = $pimple['foo']; + + $pimple['foo'] = function () { + return 'bar'; + }; + } + + /** + * @group legacy + * @expectedException \RuntimeException + * @expectedExceptionMessage Cannot override frozen service "foo". + */ + public function testLegacyOverridingServiceAfterFreeze() + { + $pimple = new Container(); + $pimple['foo'] = function () { + return 'foo'; + }; + $foo = $pimple['foo']; + + $pimple['foo'] = function () { + return 'bar'; + }; + } + + public function testRemovingServiceAfterFreeze() + { + $pimple = new Container(); + $pimple['foo'] = function () { + return 'foo'; + }; + $foo = $pimple['foo']; + + unset($pimple['foo']); + $pimple['foo'] = function () { + return 'bar'; + }; + $this->assertSame('bar', $pimple['foo']); + } + + public function testExtendingService() + { + $pimple = new Container(); + $pimple['foo'] = function () { + return 'foo'; + }; + $pimple['foo'] = $pimple->extend('foo', function ($foo, $app) { + return "$foo.bar"; + }); + $pimple['foo'] = $pimple->extend('foo', function ($foo, $app) { + return "$foo.baz"; + }); + $this->assertSame('foo.bar.baz', $pimple['foo']); + } + + public function testExtendingServiceAfterOtherServiceFreeze() + { + $pimple = new Container(); + $pimple['foo'] = function () { + return 'foo'; + }; + $pimple['bar'] = function () { + return 'bar'; + }; + $foo = $pimple['foo']; + + $pimple['bar'] = $pimple->extend('bar', function ($bar, $app) { + return "$bar.baz"; + }); + $this->assertSame('bar.baz', $pimple['bar']); + } +} diff --git a/lib/silex/vendor/pimple/pimple/src/Pimple/Tests/Psr11/ContainerTest.php b/lib/silex/vendor/pimple/pimple/src/Pimple/Tests/Psr11/ContainerTest.php new file mode 100644 index 000000000..7ca2d7fff --- /dev/null +++ b/lib/silex/vendor/pimple/pimple/src/Pimple/Tests/Psr11/ContainerTest.php @@ -0,0 +1,77 @@ +assertSame($pimple['service'], $psr->get('service')); + } + + /** + * @expectedException \Psr\Container\NotFoundExceptionInterface + * @expectedExceptionMessage Identifier "service" is not defined. + */ + public function testGetThrowsExceptionIfServiceIsNotFound() + { + $pimple = new Container(); + $psr = new PsrContainer($pimple); + + $psr->get('service'); + } + + public function testHasReturnsTrueIfServiceExists() + { + $pimple = new Container(); + $pimple['service'] = function () { + return new Service(); + }; + $psr = new PsrContainer($pimple); + + $this->assertTrue($psr->has('service')); + } + + public function testHasReturnsFalseIfServiceDoesNotExist() + { + $pimple = new Container(); + $psr = new PsrContainer($pimple); + + $this->assertFalse($psr->has('service')); + } +} diff --git a/lib/silex/vendor/pimple/pimple/src/Pimple/Tests/Psr11/ServiceLocatorTest.php b/lib/silex/vendor/pimple/pimple/src/Pimple/Tests/Psr11/ServiceLocatorTest.php new file mode 100644 index 000000000..c9a081259 --- /dev/null +++ b/lib/silex/vendor/pimple/pimple/src/Pimple/Tests/Psr11/ServiceLocatorTest.php @@ -0,0 +1,134 @@ + + */ +class ServiceLocatorTest extends TestCase +{ + public function testCanAccessServices() + { + $pimple = new Container(); + $pimple['service'] = function () { + return new Fixtures\Service(); + }; + $locator = new ServiceLocator($pimple, array('service')); + + $this->assertSame($pimple['service'], $locator->get('service')); + } + + public function testCanAccessAliasedServices() + { + $pimple = new Container(); + $pimple['service'] = function () { + return new Fixtures\Service(); + }; + $locator = new ServiceLocator($pimple, array('alias' => 'service')); + + $this->assertSame($pimple['service'], $locator->get('alias')); + } + + /** + * @expectedException \Pimple\Exception\UnknownIdentifierException + * @expectedExceptionMessage Identifier "service" is not defined. + */ + public function testCannotAccessAliasedServiceUsingRealIdentifier() + { + $pimple = new Container(); + $pimple['service'] = function () { + return new Fixtures\Service(); + }; + $locator = new ServiceLocator($pimple, array('alias' => 'service')); + + $service = $locator->get('service'); + } + + /** + * @expectedException \Pimple\Exception\UnknownIdentifierException + * @expectedExceptionMessage Identifier "foo" is not defined. + */ + public function testGetValidatesServiceCanBeLocated() + { + $pimple = new Container(); + $pimple['service'] = function () { + return new Fixtures\Service(); + }; + $locator = new ServiceLocator($pimple, array('alias' => 'service')); + + $service = $locator->get('foo'); + } + + /** + * @expectedException \Pimple\Exception\UnknownIdentifierException + * @expectedExceptionMessage Identifier "invalid" is not defined. + */ + public function testGetValidatesTargetServiceExists() + { + $pimple = new Container(); + $pimple['service'] = function () { + return new Fixtures\Service(); + }; + $locator = new ServiceLocator($pimple, array('alias' => 'invalid')); + + $service = $locator->get('alias'); + } + + public function testHasValidatesServiceCanBeLocated() + { + $pimple = new Container(); + $pimple['service1'] = function () { + return new Fixtures\Service(); + }; + $pimple['service2'] = function () { + return new Fixtures\Service(); + }; + $locator = new ServiceLocator($pimple, array('service1')); + + $this->assertTrue($locator->has('service1')); + $this->assertFalse($locator->has('service2')); + } + + public function testHasChecksIfTargetServiceExists() + { + $pimple = new Container(); + $pimple['service'] = function () { + return new Fixtures\Service(); + }; + $locator = new ServiceLocator($pimple, array('foo' => 'service', 'bar' => 'invalid')); + + $this->assertTrue($locator->has('foo')); + $this->assertFalse($locator->has('bar')); + } +} diff --git a/lib/silex/vendor/pimple/pimple/src/Pimple/Tests/ServiceIteratorTest.php b/lib/silex/vendor/pimple/pimple/src/Pimple/Tests/ServiceIteratorTest.php new file mode 100644 index 000000000..5dd52f0d9 --- /dev/null +++ b/lib/silex/vendor/pimple/pimple/src/Pimple/Tests/ServiceIteratorTest.php @@ -0,0 +1,52 @@ +assertSame(array('service1' => $pimple['service1'], 'service2' => $pimple['service2']), iterator_to_array($iterator)); + } +} diff --git a/lib/silex/vendor/psr/container/.gitignore b/lib/silex/vendor/psr/container/.gitignore new file mode 100644 index 000000000..b2395aa05 --- /dev/null +++ b/lib/silex/vendor/psr/container/.gitignore @@ -0,0 +1,3 @@ +composer.lock +composer.phar +/vendor/ diff --git a/lib/silex/vendor/psr/container/LICENSE b/lib/silex/vendor/psr/container/LICENSE new file mode 100644 index 000000000..2877a4894 --- /dev/null +++ b/lib/silex/vendor/psr/container/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2013-2016 container-interop +Copyright (c) 2016 PHP Framework Interoperability Group + +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/lib/silex/vendor/psr/container/README.md b/lib/silex/vendor/psr/container/README.md new file mode 100644 index 000000000..084f6df51 --- /dev/null +++ b/lib/silex/vendor/psr/container/README.md @@ -0,0 +1,5 @@ +# PSR Container + +This repository holds all interfaces/classes/traits related to [PSR-11](https://github.com/container-interop/fig-standards/blob/master/proposed/container.md). + +Note that this is not a container implementation of its own. See the specification for more details. diff --git a/lib/silex/vendor/psr/container/composer.json b/lib/silex/vendor/psr/container/composer.json new file mode 100644 index 000000000..b8ee01265 --- /dev/null +++ b/lib/silex/vendor/psr/container/composer.json @@ -0,0 +1,27 @@ +{ + "name": "psr/container", + "type": "library", + "description": "Common Container Interface (PHP FIG PSR-11)", + "keywords": ["psr", "psr-11", "container", "container-interop", "container-interface"], + "homepage": "https://github.com/php-fig/container", + "license": "MIT", + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "require": { + "php": ">=5.3.0" + }, + "autoload": { + "psr-4": { + "Psr\\Container\\": "src/" + } + }, + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + } +} diff --git a/lib/silex/vendor/psr/container/src/ContainerExceptionInterface.php b/lib/silex/vendor/psr/container/src/ContainerExceptionInterface.php new file mode 100644 index 000000000..d35c6b4d8 --- /dev/null +++ b/lib/silex/vendor/psr/container/src/ContainerExceptionInterface.php @@ -0,0 +1,13 @@ + " + * This must return the log messages in order. * - * Example ->error('Foo') would yield "error Foo" + * The simple formatting of the messages is: " ". + * + * Example ->error('Foo') would yield "error Foo". * * @return string[] */ - abstract function getLogs(); + abstract public function getLogs(); public function testImplements() { @@ -61,7 +65,7 @@ abstract class LoggerInterfaceTest extends \PHPUnit_Framework_TestCase } /** - * @expectedException Psr\Log\InvalidArgumentException + * @expectedException \Psr\Log\InvalidArgumentException */ public function testThrowsOnInvalidLevel() { @@ -80,12 +84,19 @@ abstract class LoggerInterfaceTest extends \PHPUnit_Framework_TestCase public function testObjectCastToString() { - $dummy = $this->getMock('Psr\Log\Test\DummyTest', array('__toString')); + if (method_exists($this, 'createPartialMock')) { + $dummy = $this->createPartialMock('Psr\Log\Test\DummyTest', array('__toString')); + } else { + $dummy = $this->getMock('Psr\Log\Test\DummyTest', array('__toString')); + } $dummy->expects($this->once()) ->method('__toString') ->will($this->returnValue('DUMMY')); $this->getLogger()->warning($dummy); + + $expected = array('warning DUMMY'); + $this->assertEquals($expected, $this->getLogs()); } public function testContextCanContainAnything() @@ -102,15 +113,28 @@ abstract class LoggerInterfaceTest extends \PHPUnit_Framework_TestCase ); $this->getLogger()->warning('Crazy context data', $context); + + $expected = array('warning Crazy context data'); + $this->assertEquals($expected, $this->getLogs()); } public function testContextExceptionKeyCanBeExceptionOrOtherValues() { - $this->getLogger()->warning('Random message', array('exception' => 'oops')); - $this->getLogger()->critical('Uncaught Exception!', array('exception' => new \LogicException('Fail'))); + $logger = $this->getLogger(); + $logger->warning('Random message', array('exception' => 'oops')); + $logger->critical('Uncaught Exception!', array('exception' => new \LogicException('Fail'))); + + $expected = array( + 'warning Random message', + 'critical Uncaught Exception!' + ); + $this->assertEquals($expected, $this->getLogs()); } } class DummyTest { -} \ No newline at end of file + public function __toString() + { + } +} diff --git a/lib/silex/vendor/psr/log/composer.json b/lib/silex/vendor/psr/log/composer.json index 6bdcc2194..87934d707 100644 --- a/lib/silex/vendor/psr/log/composer.json +++ b/lib/silex/vendor/psr/log/composer.json @@ -2,6 +2,7 @@ "name": "psr/log", "description": "Common interface for logging libraries", "keywords": ["psr", "psr-3", "log"], + "homepage": "https://github.com/php-fig/log", "license": "MIT", "authors": [ { @@ -9,9 +10,17 @@ "homepage": "http://www.php-fig.org/" } ], + "require": { + "php": ">=5.3.0" + }, "autoload": { - "psr-0": { - "Psr\\Log\\": "" + "psr-4": { + "Psr\\Log\\": "Psr/Log/" + } + }, + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" } } } diff --git a/lib/silex/vendor/silex/silex/.travis.yml b/lib/silex/vendor/silex/silex/.travis.yml new file mode 100644 index 000000000..f54aba1b7 --- /dev/null +++ b/lib/silex/vendor/silex/silex/.travis.yml @@ -0,0 +1,60 @@ +language: php + +sudo: false + +env: + global: + - SYMFONY_DEPRECATIONS_HELPER=weak + +cache: + directories: + - $HOME/.composer/cache/files + - .phpunit + +before_install: + - if [[ $TRAVIS_PHP_VERSION != hhvm ]]; then phpenv config-rm xdebug.ini; fi + +before_script: + # Twig 1.x + - if [[ $TWIG_VERSION != 2.0 ]]; then sed -i 's/~1.8|~2.0/~1.8/g' composer.json; fi + + # Symfony 2.8 + - if [[ $SYMFONY_DEPS_VERSION = 2.8 ]]; then sed -i 's/~2\.8|^3\.0/2.8.*@dev/g' composer.json; fi + # Symfony 3.0 + - if [[ $SYMFONY_DEPS_VERSION = 3.0 ]]; then sed -i 's/~2\.8|^3\.0/3.0.*@dev/g' composer.json; fi + # Symfony 3.1 + - if [[ $SYMFONY_DEPS_VERSION = 3.1 ]]; then sed -i 's/~2\.8|^3\.0/3.1.*@dev/g' composer.json; fi + # Symfony 3.2 + - if [[ $SYMFONY_DEPS_VERSION = 3.2 ]]; then sed -i 's/~2\.8|^3\.0/3.2.*@dev/g' composer.json; fi + # Symfony 3.3 + - | + if [[ $SYMFONY_DEPS_VERSION = 3.3 ]]; then + sed -i 's/~2\.8|^3\.0/3.3.*@dev/g' composer.json; + composer require --dev --no-update symfony/web-link:3.3.* + fi + + - composer update --no-suggest + +script: ./vendor/bin/simple-phpunit + +matrix: + include: + - php: 5.5 + - php: 5.6 + env: TWIG_VERSION=2.0 + - php: 5.6 + env: SYMFONY_DEPS_VERSION=2.8 + - php: 5.6 + env: SYMFONY_DEPS_VERSION=3.0 + - php: 5.6 + env: SYMFONY_DEPS_VERSION=3.1 + - php: 5.6 + env: SYMFONY_DEPS_VERSION=3.2 + - php: 5.6 + env: SYMFONY_DEPS_VERSION=3.3 + - php: 5.6 + - php: 7.0 + - php: 7.1 + - php: 7.2 + - php: hhvm + dist: trusty diff --git a/lib/silex/vendor/silex/silex/LICENSE b/lib/silex/vendor/silex/silex/LICENSE index bc6ad0497..b420d7195 100644 --- a/lib/silex/vendor/silex/silex/LICENSE +++ b/lib/silex/vendor/silex/silex/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2010-2015 Fabien Potencier +Copyright (c) 2010-2017 Fabien Potencier 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/lib/silex/vendor/silex/silex/README.rst b/lib/silex/vendor/silex/silex/README.rst index c7b71d3b3..b79e47b6d 100644 --- a/lib/silex/vendor/silex/silex/README.rst +++ b/lib/silex/vendor/silex/silex/README.rst @@ -1,7 +1,7 @@ Silex, a simple Web Framework ============================= -Silex is a PHP micro-framework to develop websites based on `Symfony2 +Silex is a PHP micro-framework to develop websites based on `Symfony components`_: .. code-block:: php @@ -18,7 +18,7 @@ components`_: $app->run(); -Silex works with PHP 5.3.3 or later. +Silex works with PHP 5.5.9 or later. Installation ------------ @@ -27,14 +27,15 @@ The recommended way to install Silex is through `Composer`_: .. code-block:: bash - php composer.phar require silex/silex "~1.2" + composer require silex/silex "~2.0" Alternatively, you can download the `silex.zip`_ file and extract it. More Information ---------------- -Read the `documentation`_ for more information. +Read the `documentation`_ for more information and `changelog +`_ for upgrading information. Tests ----- @@ -43,8 +44,8 @@ To run the test suite, you need `Composer`_ and `PHPUnit`_: .. code-block:: bash - $ composer install - $ phpunit + composer install + phpunit Community --------- @@ -56,8 +57,8 @@ License Silex is licensed under the MIT license. -.. _Symfony2 components: http://symfony.com -.. _Composer: http://getcomposer.org -.. _PHPUnit: https://phpunit.de -.. _silex.zip: http://silex.sensiolabs.org/download -.. _documentation: http://silex.sensiolabs.org/documentation +.. _Symfony components: http://symfony.com +.. _Composer: http://getcomposer.org +.. _PHPUnit: https://phpunit.de +.. _silex.zip: http://silex.sensiolabs.org/download +.. _documentation: http://silex.sensiolabs.org/documentation diff --git a/lib/silex/vendor/silex/silex/composer.json b/lib/silex/vendor/silex/silex/composer.json index d73236a86..935c51812 100644 --- a/lib/silex/vendor/silex/silex/composer.json +++ b/lib/silex/vendor/silex/silex/composer.json @@ -1,7 +1,6 @@ { - "minimum-stability": "dev", "name": "silex/silex", - "description": "The PHP micro-framework based on the Symfony2 Components", + "description": "The PHP micro-framework based on the Symfony Components", "keywords": ["microframework"], "homepage": "http://silex.sensiolabs.org", "license": "MIT", @@ -16,47 +15,58 @@ } ], "require": { - "php": ">=5.3.3", - "pimple/pimple": "~1.0", - "symfony/event-dispatcher": "~2.3,<2.7", - "symfony/http-foundation": "~2.3,<2.7", - "symfony/http-kernel": "~2.3,<2.7", - "symfony/routing": "~2.3,<2.7" + "php": ">=5.5.9", + "pimple/pimple": "~3.0", + "symfony/event-dispatcher": "~2.8|^3.0", + "symfony/http-foundation": "~2.8|^3.0", + "symfony/http-kernel": "~2.8|^3.0", + "symfony/routing": "~2.8|^3.0" }, "require-dev": { - "symfony/security": "~2.3,<2.7", - "symfony/config": "~2.3,<2.7", - "symfony/locale": "~2.3,<2.7", - "symfony/form": "~2.3,<2.7", - "symfony/browser-kit": "~2.3,<2.7", - "symfony/css-selector": "~2.3,<2.7", - "symfony/debug": "~2.3,<2.7", - "symfony/dom-crawler": "~2.3,<2.7", - "symfony/finder": "~2.3,<2.7", - "symfony/monolog-bridge": "~2.3,<2.7", - "symfony/options-resolver": "~2.3,<2.7", - "symfony/process": "~2.3,<2.7", - "symfony/serializer": "~2.3,<2.7", - "symfony/translation": "~2.3,<2.7", - "symfony/twig-bridge": "~2.3,<2.7", - "symfony/validator": "~2.3,<2.7", - "twig/twig": ">=1.8.0,<2.0-dev", + "symfony/asset": "~2.8|^3.0", + "symfony/expression-language": "~2.8|^3.0", + "symfony/security": "~2.8|^3.0", + "symfony/config": "~2.8|^3.0", + "symfony/form": "~2.8|^3.0", + "symfony/browser-kit": "~2.8|^3.0", + "symfony/css-selector": "~2.8|^3.0", + "symfony/debug": "~2.8|^3.0", + "symfony/dom-crawler": "~2.8|^3.0", + "symfony/finder": "~2.8|^3.0", + "symfony/intl": "~2.8|^3.0", + "symfony/monolog-bridge": "~2.8|^3.0", + "symfony/doctrine-bridge": "~2.8|^3.0", + "symfony/options-resolver": "~2.8|^3.0", + "symfony/phpunit-bridge": "^3.2", + "symfony/process": "~2.8|^3.0", + "symfony/serializer": "~2.8|^3.0", + "symfony/translation": "~2.8|^3.0", + "symfony/twig-bridge": "~2.8|^3.0", + "symfony/validator": "~2.8|^3.0", + "symfony/var-dumper": "~2.8|^3.0", + "twig/twig": "~1.28|~2.0", "doctrine/dbal": "~2.2", - "swiftmailer/swiftmailer": "5.*", - "monolog/monolog": "~1.4,>=1.4.1" + "swiftmailer/swiftmailer": "~5", + "monolog/monolog": "^1.4.1", + "symfony/web-link": "^3.3" }, - "suggest": { - "symfony/browser-kit": "~2.3", - "symfony/css-selector": "~2.3", - "symfony/dom-crawler": "~2.3", - "symfony/form": "~2.3" + "conflict": { + "phpunit/phpunit": "<4.8.35 || >= 5.0, <5.4.3" + }, + "replace": { + "silex/api": "self.version", + "silex/providers": "self.version" }, "autoload": { - "psr-0": { "Silex": "src/" } + "psr-4": { "Silex\\": "src/Silex" } + }, + "autoload-dev" : { + "psr-4": { "Silex\\Tests\\" : "tests/Silex/Tests" } }, "extra": { "branch-alias": { - "dev-master": "1.2.x-dev" + "dev-master": "2.2.x-dev" } - } + }, + "minimum-stability": "dev" } diff --git a/lib/silex/vendor/silex/silex/doc/changelog.rst b/lib/silex/vendor/silex/silex/doc/changelog.rst index a721aafc3..01a2cb86d 100644 --- a/lib/silex/vendor/silex/silex/doc/changelog.rst +++ b/lib/silex/vendor/silex/silex/doc/changelog.rst @@ -1,6 +1,145 @@ Changelog ========= +2.2.2 (2018-01-12) +------------------ + +* [SECURITY] fixed before handlers not executed under mounts + +2.2.1 (2017-12-14) +------------------ + +* added support for Swiftmailer SSL stream_context_options option +* fixed usage of namespaces for Twig paths + +2.2.0 (2017-07-23) +------------------ + +* added json manifest version strategy support +* fixed EsiFragment constructor +* fixed RedirectableUrlMatcher compatibility with Symfony +* fixed compatibility with Pimple 3.2 +* fixed WebTestCase compatibility with PHPUnit 6+ + +2.1.0 (2017-05-03) +------------------ + +* added more options to security.firewalls +* added WebLink component integration +* added parameters to configure the Twig core extension behavior +* fixed deprecation notices with symfony/twig-bridge 3.2+ in TwigServiceProvider +* added FormRegistry as a service to enable the extension point +* removed the build scripts +* fixed some deprecation warnings +* added support for registering Swiftmailer plugins + +2.0.4 (2016-11-06) +------------------ + +* fixed twig.app_variable definition +* added support for latest versions of Twig 1.x and 2.0 (Twig runtime loaders) +* added support for Symfony 2.3 + +2.0.3 (2016-08-22) +------------------ + +* fixed lazy evaluation of 'monolog.use_error_handler' +* fixed PHP7 type hint on controllers + +2.0.2 (2016-06-14) +------------------ + +* fixed Symfony 3.1 deprecations + +2.0.1 (2016-05-27) +------------------ + +* fixed the silex form extension registration to allow overriding default ones +* removed support for the obsolete Locale Symfony component (uses the Intl one now) +* added support for Symfony 3.1 + +2.0.0 (2016-05-18) +------------------ + +* decoupled the exception handler from HttpKernelServiceProvider +* Switched to BCrypt as the default encoder in the security provider +* added full support for RequestMatcher +* added support for Symfony Guard +* added support for callables in CallbackResolver +* added FormTrait::namedForm() +* added support for delivery_addresses, delivery_whitelist, and sender_address +* added support to register form types / form types extensions / form types guessers as services +* added support for callable in mounts (allow nested route collection to be built easily) +* added support for conditions on routes +* added support for the Symfony VarDumper Component +* added a global Twig variable (an AppVariable instance) +* [BC BREAK] CSRF has been moved to a standalone provider (``form.secret`` is not available anymore) +* added support for the Symfony HttpFoundation Twig bridge extension +* added support for the Symfony Asset Component +* bumped minimum version of Symfony to 2.8 +* bumped minimum version of PHP to 5.5.0 +* Updated Pimple to 3.0 +* Updated session listeners to extends HttpKernel ones +* [BC BREAK] Locale management has been moved to LocaleServiceProvider which must be registered + if you want Silex to manage your locale (must also be registered for the translation service provider) +* [BC BREAK] Provider interfaces moved to Silex\Api namespace, published as + separate package via subtree split +* [BC BREAK] ServiceProviderInterface split in to EventListenerProviderInterface + and BootableProviderInterface +* [BC BREAK] Service Provider support files moved under Silex\Provider + namespace, allowing publishing as separate package via sub-tree split +* ``monolog.exception.logger_filter`` option added to Monolog service provider +* [BC BREAK] ``$app['request']`` service removed, use ``$app['request_stack']`` instead + +1.3.6 (2016-XX-XX) +------------------ + +* n/a + +1.3.5 (2016-01-06) +------------------ + +* fixed typo in SecurityServiceProvider + +1.3.4 (2015-09-15) +------------------ + +* fixed some new deprecations +* fixed translation registration for the validators + +1.3.3 (2015-09-08) +------------------ + +* added support for Symfony 3.0 and Twig 2.0 +* fixed some Form deprecations +* removed deprecated method call in the exception handler +* fixed Swiftmailer spool flushing when spool is not enabled + +1.3.2 (2015-08-24) +------------------ + +* no changes + +1.3.1 (2015-08-04) +------------------ + +* added missing support for the Expression constraint +* fixed the possibility to override translations for validator error messages +* fixed sub-mounts with same name clash +* fixed session logout handler when a firewall is stateless + +1.3.0 (2015-06-05) +------------------ + +* added a `$app['user']` to get the current user (security provider) +* added view handlers +* added support for the OPTIONS HTTP method +* added caching for the Translator provider +* deprecated `$app['exception_handler']->disable()` in favor of `unset($app['exception_handler'])` +* made Silex compatible with Symfony 2.7 an 2.8 (and keep compatibility with Symfony 2.3, 2.5, and 2.6) +* removed deprecated TwigCoreExtension class (register the new HttpFragmentServiceProvider instead) +* bumped minimum version of PHP to 5.3.9 + 1.2.5 (2015-06-04) ------------------ diff --git a/lib/silex/vendor/silex/silex/doc/contributing.rst b/lib/silex/vendor/silex/silex/doc/contributing.rst index d584fc115..34a339d86 100644 --- a/lib/silex/vendor/silex/silex/doc/contributing.rst +++ b/lib/silex/vendor/silex/silex/doc/contributing.rst @@ -1,47 +1,26 @@ Contributing ============ -We are open to contributions to the Silex code. If you find -a bug or want to contribute a provider, just follow these -steps. +We are open to contributions to the Silex code. If you find a bug or want to +contribute a provider, just follow these steps: -* Fork `the Silex repository `_ - on github. +* Fork `the Silex repository `_; -* Make your feature addition or bug fix. +* Make your feature addition or bug fix; -* Add tests for it. This is important so we don't break it in a future version unintentionally. +* Add tests for it; -* Optionally, add some technical documentation. +* Optionally, add some documentation; -* `Send a pull request `_, to the correct `target branch`_. - Bonus points for topic branches. - -If you have a big change or would like to discuss something, -please join us on the `mailing list -`_. +* `Send a pull request + `_, to the correct + target branch (1.3 for bug fixes, master for new features). .. note:: Any code you contribute must be licensed under the MIT License. -Target branch -============= - -Before you create a pull request for Silex, you need to determine which branch -to submit it to. Read this section carefully first. - -Silex has two active branches: `1.0` and `master` (`1.1`). - -* **1.0**: Bugfixes and documentation fixes go into the 1.0 branch. 1.0 is - periodically merged into master. The 1.0 branch targets versions 2.1, 2.2 and - 2.3 of Symfony2. - -* **1.1**: All new features go into the 1.1 branch. Changes cannot break - backward compatibility. The 1.1 branch targets the 2.3 version of Symfony2. - - Writing Documentation ===================== diff --git a/lib/silex/vendor/silex/silex/doc/cookbook/error_handler.rst b/lib/silex/vendor/silex/silex/doc/cookbook/error_handler.rst index 212ec98f2..235c263a7 100644 --- a/lib/silex/vendor/silex/silex/doc/cookbook/error_handler.rst +++ b/lib/silex/vendor/silex/silex/doc/cookbook/error_handler.rst @@ -1,33 +1,24 @@ Converting Errors to Exceptions =============================== -Silex will catch exceptions that are thrown from within a request/response -cycle. It will however *not* catch PHP errors and notices. You can catch them -by converting them to exceptions, this recipe will tell you how. - -Why does Silex not do this? ---------------------------- - -Silex could do this automatically in theory, but there is a reason why it does -not. Silex acts as a library, this means that it does not mess with any global -state. Since error handlers are global in PHP, it is your responsibility as a -user to register them. +Silex catches exceptions that are thrown from within a request/response cycle. +However, it does *not* catch PHP errors and notices. This recipe tells you how +to catch them by converting them to exceptions. Registering the ErrorHandler ---------------------------- -Fortunately, the ``Symfony/Debug`` package has an ``ErrorHandler`` class that -solves this issue. It converts all errors to exceptions, and exceptions can be -caught by Silex. +The ``Symfony/Debug`` package has an ``ErrorHandler`` class that solves this +problem. It converts all errors to exceptions, and exceptions are then caught +by Silex. -You register it by calling the static ``register`` method:: +Register it by calling the static ``register`` method:: use Symfony\Component\Debug\ErrorHandler; ErrorHandler::register(); -It is recommended that you do this in your front controller, i.e. -``web/index.php``. +It is recommended that you do this as early as possible. Handling fatal errors --------------------- diff --git a/lib/silex/vendor/silex/silex/doc/cookbook/form_no_csrf.rst b/lib/silex/vendor/silex/silex/doc/cookbook/form_no_csrf.rst index 46b22c697..e9bf595e4 100644 --- a/lib/silex/vendor/silex/silex/doc/cookbook/form_no_csrf.rst +++ b/lib/silex/vendor/silex/silex/doc/cookbook/form_no_csrf.rst @@ -2,13 +2,14 @@ Disabling CSRF Protection on a Form using the FormExtension =========================================================== The *FormExtension* provides a service for building form in your application -with the Symfony2 Form component. By default, the *FormExtension* uses the -CSRF Protection avoiding Cross-site request forgery, a method by which a -malicious user attempts to make your legitimate users unknowingly submit data -that they don't intend to submit. +with the Symfony Form component. When the :doc:`CSRF Service Provider +` is registered, the *FormExtension* uses the CSRF Protection +avoiding Cross-site request forgery, a method by which a malicious user +attempts to make your legitimate users unknowingly submit data that they don't +intend to submit. You can find more details about CSRF Protection and CSRF token in the -`Symfony2 Book +`Symfony Book `_. In some cases (for example, when embedding a form in an html email) you might @@ -30,6 +31,6 @@ Going further This specific example showed how to change the ``csrf_protection`` in the ``$options`` parameter of the ``createBuilder()`` function. More of them could -be passed through this parameter, it is as simple as using the Symfony2 +be passed through this parameter, it is as simple as using the Symfony ``getDefaultOptions()`` method in your form classes. `See more here `_. diff --git a/lib/silex/vendor/silex/silex/doc/cookbook/guard_authentication.rst b/lib/silex/vendor/silex/silex/doc/cookbook/guard_authentication.rst new file mode 100644 index 000000000..8774f6863 --- /dev/null +++ b/lib/silex/vendor/silex/silex/doc/cookbook/guard_authentication.rst @@ -0,0 +1,183 @@ +How to Create a Custom Authentication System with Guard +======================================================= + +Whether you need to build a traditional login form, an API token +authentication system or you need to integrate with some proprietary +single-sign-on system, the Guard component can make it easy... and fun! + +In this example, you'll build an API token authentication system and +learn how to work with Guard. + +Step 1) Create the Authenticator Class +-------------------------------------- + +Suppose you have an API where your clients will send an X-AUTH-TOKEN +header on each request. This token is composed of the username followed +by a password, separated by a colon (e.g. ``X-AUTH-TOKEN: coolguy:awesomepassword``). +Your job is to read this, find the associated user (if any) and check +the password. + +To create a custom authentication system, just create a class and make +it implement GuardAuthenticatorInterface. Or, extend the simpler +AbstractGuardAuthenticator. This requires you to implement six methods: + +.. code-block:: php + + encoderFactory = $encoderFactory; + } + + public function getCredentials(Request $request) + { + // Checks if the credential header is provided + if (!$token = $request->headers->get('X-AUTH-TOKEN')) { + return; + } + + // Parse the header or ignore it if the format is incorrect. + if (false === strpos($token, ':')) { + return; + } + list($username, $secret) = explode(':', $token, 2); + + return array( + 'username' => $username, + 'secret' => $secret, + ); + } + + public function getUser($credentials, UserProviderInterface $userProvider) + { + return $userProvider->loadUserByUsername($credentials['username']); + } + + public function checkCredentials($credentials, UserInterface $user) + { + // check credentials - e.g. make sure the password is valid + // return true to cause authentication success + + $encoder = $this->encoderFactory->getEncoder($user); + + return $encoder->isPasswordValid( + $user->getPassword(), + $credentials['secret'], + $user->getSalt() + ); + } + + public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey) + { + // on success, let the request continue + return; + } + + public function onAuthenticationFailure(Request $request, AuthenticationException $exception) + { + $data = array( + 'message' => strtr($exception->getMessageKey(), $exception->getMessageData()), + + // or to translate this message + // $this->translator->trans($exception->getMessageKey(), $exception->getMessageData()) + ); + + return new JsonResponse($data, 403); + } + + /** + * Called when authentication is needed, but it's not sent + */ + public function start(Request $request, AuthenticationException $authException = null) + { + $data = array( + // you might translate this message + 'message' => 'Authentication Required', + ); + + return new JsonResponse($data, 401); + } + + public function supportsRememberMe() + { + return false; + } + } + + +Step 2) Configure the Authenticator +----------------------------------- + +To finish this, register the class as a service: + +.. code-block:: php + + $app['app.token_authenticator'] = function ($app) { + return new App\Security\TokenAuthenticator($app['security.encoder_factory']); + }; + + +Finally, configure your `security.firewalls` key to use this authenticator: + +.. code-block:: php + + $app['security.firewalls'] = array( + 'main' => array( + 'guard' => array( + 'authenticators' => array( + 'app.token_authenticator' + ), + + // Using more than 1 authenticator, you must specify + // which one is used as entry point. + // 'entry_point' => 'app.token_authenticator', + ), + // configure where your users come from. Hardcode them, or load them from somewhere + // http://silex.sensiolabs.org/doc/providers/security.html#defining-a-custom-user-provider + 'users' => array( + //raw password = foo + 'victoria' => array('ROLE_USER', '$2y$10$3i9/lVd8UOFIJ6PAMFt8gu3/r5g0qeCJvoSlLCsvMTythye19F77a'), + ), + // 'anonymous' => true + ), + ); + +.. note:: + You can use many authenticators, they are executed by the order + they are configured. + +You did it! You now have a fully-working API token authentication +system. If your homepage required ROLE_USER, then you could test it +under different conditions: + +.. code-block:: bash + + # test with no token + curl http://localhost:8000/ + # {"message":"Authentication Required"} + + # test with a bad token + curl -H "X-AUTH-TOKEN: alan" http://localhost:8000/ + # {"message":"Username could not be found."} + + # test with a working token + curl -H "X-AUTH-TOKEN: victoria:foo" http://localhost:8000/ + # the homepage controller is executed: the page loads normally + +For more details read the Symfony cookbook entry on +`How to Create a Custom Authentication System with Guard `_. diff --git a/lib/silex/vendor/silex/silex/doc/cookbook/index.rst b/lib/silex/vendor/silex/silex/doc/cookbook/index.rst index a89b014c8..53b10fe2c 100644 --- a/lib/silex/vendor/silex/silex/doc/cookbook/index.rst +++ b/lib/silex/vendor/silex/silex/doc/cookbook/index.rst @@ -8,14 +8,13 @@ The cookbook section contains recipes for solving specific problems. :hidden: json_request_body - translating_validation_messages session_storage form_no_csrf validator_yaml sub_requests error_handler multiple_loggers - assets + guard_authentication Recipes ------- @@ -24,8 +23,6 @@ Recipes building a restful API is the ability to accept a JSON encoded entity from the request body. -* :doc:`Translating Validation Messages`. - * :doc:`Using PdoSessionStorage to store Sessions in the Database `. @@ -40,4 +37,4 @@ Recipes * :doc:`Using multiple Monolog Loggers `. -* :doc:`Managing Assets in Templates `. +* :doc:`How to Create a Custom Authentication System with Guard `. diff --git a/lib/silex/vendor/silex/silex/doc/cookbook/multiple_loggers.rst b/lib/silex/vendor/silex/silex/doc/cookbook/multiple_loggers.rst index 83b91faa6..cb1039530 100644 --- a/lib/silex/vendor/silex/silex/doc/cookbook/multiple_loggers.rst +++ b/lib/silex/vendor/silex/silex/doc/cookbook/multiple_loggers.rst @@ -1,12 +1,12 @@ Using multiple Monolog Loggers ============================== -Having separate instances of `Monolog` for different parts of your system is +Having separate instances of Monolog for different parts of your system is often desirable and allows you to configure them independently, allowing for fine grained control of where your logging goes and in what detail. This simple example allows you to quickly configure several monolog instances, -using the bundled handler, but each with a different channel. +using the bundled handler, but each with a different channel. .. code-block:: php @@ -18,9 +18,9 @@ using the bundled handler, but each with a different channel. }); foreach (array('auth', 'payments', 'stats') as $channel) { - $app['monolog.'.$channel] = $app->share(function ($app) use ($channel) { + $app['monolog.'.$channel] = function ($app) use ($channel) { return $app['monolog.factory']($channel); - }); + }; } As your application grows, or your logging needs for certain areas of the @@ -31,13 +31,13 @@ particular service separately, including your customizations. use Monolog\Handler\StreamHandler; - $app['monolog.payments'] = $app->share(function ($app) { + $app['monolog.payments'] = function ($app) { $log = new $app['monolog.logger.class']('payments'); $handler = new StreamHandler($app['monolog.payments.logfile'], $app['monolog.payment.level']); $log->pushHandler($handler); return $log; - }); + }; Alternatively, you could attempt to make the factory more complicated, and rely on some conventions, such as checking for an array of handlers registered with @@ -62,10 +62,8 @@ the container with the channel name, defaulting to the bundled handler. return $log; }); - $app['monolog.payments.handlers'] = $app->share(function ($app) { + $app['monolog.payments.handlers'] = function ($app) { return array( new StreamHandler(__DIR__.'/../payments.log', Logger::DEBUG), ); - }); - - + }; diff --git a/lib/silex/vendor/silex/silex/doc/cookbook/session_storage.rst b/lib/silex/vendor/silex/silex/doc/cookbook/session_storage.rst index 6f20f3f58..29328b490 100644 --- a/lib/silex/vendor/silex/silex/doc/cookbook/session_storage.rst +++ b/lib/silex/vendor/silex/silex/doc/cookbook/session_storage.rst @@ -2,12 +2,11 @@ Using PdoSessionStorage to store Sessions in the Database ========================================================= By default, the :doc:`SessionServiceProvider ` writes -session information in files using Symfony2 NativeFileSessionStorage. Most +session information in files using Symfony NativeFileSessionStorage. Most medium to large websites use a database to store sessions instead of files, -because databases are easier to use and scale in a multi-webserver -environment. +because databases are easier to use and scale in a multi-webserver environment. -Symfony2's `NativeSessionStorage +Symfony's `NativeSessionStorage `_ has multiple storage handlers and one of them uses PDO to store sessions, `PdoSessionHandler @@ -35,21 +34,20 @@ With a dedicated PDO service 'db_time_col' => 'session_time', ); - $app['pdo'] = $app->share(function () use ($app) { + $app['pdo'] = function () use ($app) { return new PDO( $app['pdo.dsn'], $app['pdo.user'], $app['pdo.password'] ); - }); + }; - $app['session.storage.handler'] = $app->share(function () use ($app) { + $app['session.storage.handler'] = function () use ($app) { return new PdoSessionHandler( $app['pdo'], - $app['session.db_options'], - $app['session.storage.options'] + $app['session.db_options'] ); - }); + }; Using the DoctrineServiceProvider --------------------------------- @@ -63,20 +61,18 @@ have to make another database connection, simply pass the getWrappedConnection m $app->register(new Silex\Provider\SessionServiceProvider()); - $app['session.db_options'] = array( - 'db_table' => 'session', - 'db_id_col' => 'session_id', - 'db_data_col' => 'session_value', - 'db_time_col' => 'session_time', - ); - - $app['session.storage.handler'] = $app->share(function () use ($app) { + $app['session.storage.handler'] = function () use ($app) { return new PdoSessionHandler( $app['db']->getWrappedConnection(), - $app['session.db_options'], - $app['session.storage.options'] + array( + 'db_table' => 'session', + 'db_id_col' => 'session_id', + 'db_data_col' => 'session_value', + 'db_lifetime_col' => 'session_lifetime', + 'db_time_col' => 'session_time', + ) ); - }); + }; Database structure ------------------ @@ -85,8 +81,9 @@ PdoSessionStorage needs a database table with 3 columns: * ``session_id``: ID column (VARCHAR(255) or larger) * ``session_value``: Value column (TEXT or CLOB) +* ``session_lifetime``: Lifetime column (INTEGER) * ``session_time``: Time column (INTEGER) You can find examples of SQL statements to create the session table in the -`Symfony2 cookbook +`Symfony cookbook `_ diff --git a/lib/silex/vendor/silex/silex/doc/cookbook/sub_requests.rst b/lib/silex/vendor/silex/silex/doc/cookbook/sub_requests.rst index 78070de86..95d391362 100644 --- a/lib/silex/vendor/silex/silex/doc/cookbook/sub_requests.rst +++ b/lib/silex/vendor/silex/silex/doc/cookbook/sub_requests.rst @@ -33,7 +33,7 @@ By calling ``handle``, you can make a sub-request manually. Here's an example:: There's some more things that you need to keep in mind though. In most cases you will want to forward some parts of the current master request to the -sub-request. That includes: Cookies, server information, session. +sub-request like cookies, server information, or the session. Here is a more advanced example that forwards said information (``$request`` holds the master request):: @@ -122,12 +122,8 @@ constructing a request:: This is something to be aware of when making sub-requests by hand. -Lack of container scopes ------------------------- - -While the sub-requests available in Silex are quite powerful, they have their -limits. The major limitation/danger that you will run into is the lack of -scopes on the Pimple container. +Services depending on the Request +--------------------------------- The container is a concept that is global to a Silex application, since the application object **is** the container. Any request that is run against an @@ -137,41 +133,5 @@ Any services depending on the ``request`` service will store the first request that they get (could be master or sub-request), and keep using it, even if that request is already over. -For example:: - - use Symfony\Component\HttpFoundation\Request; - - class ContentFormatNegotiator - { - private $request; - - public function __construct(Request $request) - { - $this->request = $request; - } - - public function negotiateFormat(array $serverTypes) - { - $clientAcceptType = $this->request->headers->get('Accept'); - - ... - - return $format; - } - } - -This example looks harmless, but it might blow up. You have no way of knowing -what ``$request->headers->get()`` will return, because ``$request`` could be -either the master request or a sub-request. The answer in this case is to pass -the request as an argument to ``negotiateFormat``. Then you can pass it in -from a location where you have safe access to the current request: a listener -or a controller. - -Here are a few general approaches to working around this issue: - -* Use ESI with Varnish. - -* Do not inject the request, ever. Use listeners instead, as they can access - the request without storing it. - -* Inject the Silex Application and fetch the request from it. +Instead of injecting the ``request`` service, you should always inject the +``request_stack`` one instead. diff --git a/lib/silex/vendor/silex/silex/doc/cookbook/validator_yaml.rst b/lib/silex/vendor/silex/silex/doc/cookbook/validator_yaml.rst index 2d478ffd0..10a41fcfa 100644 --- a/lib/silex/vendor/silex/silex/doc/cookbook/validator_yaml.rst +++ b/lib/silex/vendor/silex/silex/doc/cookbook/validator_yaml.rst @@ -16,7 +16,7 @@ Next, you need to tell the Validation Service that you are not using $app->register(new ValidatorServiceProvider()); - $app['validator.mapping.class_metadata_factory'] = new Symfony\Component\Validator\Mapping\ClassMetadataFactory( + $app['validator.mapping.class_metadata_factory'] = new Symfony\Component\Validator\Mapping\Factory\LazyLoadingMetadataFactory( new Symfony\Component\Validator\Mapping\Loader\YamlFileLoader(__DIR__.'/validation.yml') ); diff --git a/lib/silex/vendor/silex/silex/doc/index.rst b/lib/silex/vendor/silex/silex/doc/index.rst index 938623c4e..d1a851d0a 100644 --- a/lib/silex/vendor/silex/silex/doc/index.rst +++ b/lib/silex/vendor/silex/silex/doc/index.rst @@ -1,5 +1,5 @@ -Silex -===== +The Book +======== .. toctree:: :maxdepth: 1 @@ -17,4 +17,3 @@ Silex providers/index web_servers changelog - phar diff --git a/lib/silex/vendor/silex/silex/doc/internals.rst b/lib/silex/vendor/silex/silex/doc/internals.rst index e3e3e5951..c7ffac8ce 100644 --- a/lib/silex/vendor/silex/silex/doc/internals.rst +++ b/lib/silex/vendor/silex/silex/doc/internals.rst @@ -1,8 +1,7 @@ Internals ========= -This chapter will tell you a bit about how Silex works -internally. +This chapter will tell you how Silex works internally. Silex ----- @@ -10,7 +9,7 @@ Silex Application ~~~~~~~~~~~ -The application is the main interface to Silex. It implements Symfony2's +The application is the main interface to Silex. It implements Symfony's `HttpKernelInterface `_, so you can pass a `Request @@ -23,8 +22,8 @@ outside as well as the inside. You could replace any service, and you are also able to read them. The application makes strong use of the `EventDispatcher -`_ -to hook into the Symfony2 `HttpKernel +`_ to hook into the Symfony `HttpKernel `_ events. This allows fetching the ``Request``, converting string responses into ``Response`` objects and handling Exceptions. We also use it to dispatch some @@ -33,7 +32,7 @@ custom events like before/after middlewares and errors. Controller ~~~~~~~~~~ -The Symfony2 `Route +The Symfony `Route `_ is actually quite powerful. Routes can be named, which allows for URL generation. They can also have requirements for the variable parts. In order to allow @@ -69,10 +68,10 @@ The ``Application`` provides a shortcut ``flush`` method for flushing the Instead of creating an instance of ``RouteCollection`` yourself, use the ``$app['controllers_factory']`` factory instead. -Symfony2 --------- +Symfony +------- -Following Symfony2 components are used by Silex: +Following Symfony components are used by Silex: * **HttpFoundation**: For ``Request`` and ``Response``. diff --git a/lib/silex/vendor/silex/silex/doc/intro.rst b/lib/silex/vendor/silex/silex/doc/intro.rst index 160ab5adf..2ab2bc300 100644 --- a/lib/silex/vendor/silex/silex/doc/intro.rst +++ b/lib/silex/vendor/silex/silex/doc/intro.rst @@ -1,18 +1,17 @@ Introduction ============ -Silex is a PHP microframework for PHP 5.3. It is built on the shoulders of -`Symfony2`_ and `Pimple`_ and also inspired by `Sinatra`_. +Silex is a PHP microframework. It is built on the shoulders of `Symfony`_ and +`Pimple`_ and also inspired by `Sinatra`_. -A microframework provides the guts for building simple single-file apps. Silex -aims to be: +Silex aims to be: -* *Concise*: Silex exposes an intuitive and concise API that is fun to use. +* *Concise*: Silex exposes an intuitive and concise API. -* *Extensible*: Silex has an extension system based around the Pimple micro - service-container that makes it even easier to tie in third party libraries. +* *Extensible*: Silex has an extension system based around the Pimple + service-container that makes it easy to tie in third party libraries. -* *Testable*: Silex uses Symfony2's HttpKernel which abstracts request and +* *Testable*: Silex uses Symfony's HttpKernel which abstracts request and response. This makes it very easy to test apps and the framework itself. It also respects the HTTP specification and encourages its proper use. @@ -39,29 +38,13 @@ Usage All that is needed to get access to the Framework is to include the autoloader. -Next a route for ``/hello/{name}`` that matches for ``GET`` requests is defined. -When the route matches, the function is executed and the return value is sent -back to the client. +Next, a route for ``/hello/{name}`` that matches for ``GET`` requests is +defined. When the route matches, the function is executed and the return value +is sent back to the client. Finally, the app is run. Visit ``/hello/world`` to see the result. It's really that easy! -Installation ------------- - -Installing Silex is as easy as it can get: - -.. code-block:: bash - - composer require silex/silex - -Another way is to `download`_ the archive file and extract it. - -All examples in the documentation relies on a well-configured web server; read -the :doc:`webserver documentation` to check yours. - -.. _Symfony2: http://symfony.com/ +.. _Symfony: http://symfony.com/ .. _Pimple: http://pimple.sensiolabs.org/ .. _Sinatra: http://www.sinatrarb.com/ -.. _Composer: http://getcomposer.org/ -.. _`download`: http://silex.sensiolabs.org/download diff --git a/lib/silex/vendor/silex/silex/doc/middlewares.rst b/lib/silex/vendor/silex/silex/doc/middlewares.rst index 2dc6fc2b6..c5c17cf0d 100644 --- a/lib/silex/vendor/silex/silex/doc/middlewares.rst +++ b/lib/silex/vendor/silex/silex/doc/middlewares.rst @@ -1,18 +1,18 @@ -Middlewares -=========== +Middleware +========== Silex allows you to run code, that changes the default Silex behavior, at -different stages during the handling of a request through *middlewares*: +different stages during the handling of a request through *middleware*: -* *Application middlewares* are triggered independently of the current handled +* *Application middleware* is triggered independently of the current handled request; -* *Route middlewares* are triggered when their associated route is matched. +* *Route middleware* is triggered when its associated route is matched. -Application Middlewares ------------------------ +Application Middleware +---------------------- -The application middlewares are only run for the "master" Request. +Application middleware is only run for the "master" Request. Before Middleware ~~~~~~~~~~~~~~~~~ @@ -34,9 +34,8 @@ early event:: // ... }, Application::EARLY_EVENT); -Of course, in this case, the routing and the security won't have been -executed, and so you won't have access to the locale, the current route, or -the security user. +In this case, the routing and the security won't have been executed, and so you +won't have access to the locale, the current route, or the security user. .. note:: @@ -74,10 +73,10 @@ Response has been sent to the client (like sending emails or logging):: The finish middleware is an event registered on the Symfony *terminate* event. -Route Middlewares ------------------ +Route Middleware +---------------- -Route middlewares are added to routes or route collections and they are only +Route middleware is added to routes or route collections and it is only triggered when the corresponding route is matched. You can also stack them:: $app->get('/somewhere', function () { @@ -93,7 +92,7 @@ Before Middleware ~~~~~~~~~~~~~~~~~ A *before* route middleware is fired just before the route callback, but after -the *before* application middlewares:: +the *before* application middleware:: $before = function (Request $request, Application $app) { // ... @@ -108,7 +107,7 @@ After Middleware ~~~~~~~~~~~~~~~~ An *after* route middleware is fired just after the route callback, but before -the application *after* application middlewares:: +the application *after* application middleware:: $after = function (Request $request, Response $response, Application $app) { // ... @@ -119,10 +118,10 @@ the application *after* application middlewares:: }) ->after($after); -Middlewares Priority --------------------- +Middleware Priority +------------------- -You can add as many middlewares as you want, in which case they are triggered +You can add as much middleware as you want, in which case they are triggered in the same order as you added them. You can explicitly control the priority of your middleware by passing an @@ -146,9 +145,9 @@ possible or as late as possible:: Short-circuiting the Controller ------------------------------- -If a before middleware returns a Response object, the Request handling is -short-circuited (the next middlewares won't be run, nor the route -callback), and the Response is passed to the after middlewares right away:: +If a *before* middleware returns a ``Response`` object, the request handling is +short-circuited (the next middleware won't be run, nor the route +callback), and the Response is passed to the *after* middleware right away:: $app->before(function (Request $request) { // redirect the user to the login screen if access to the Resource is protected @@ -159,5 +158,5 @@ callback), and the Response is passed to the after middlewares right away:: .. note:: - If a before middleware does not return a Response or ``null``, a - ``RuntimeException`` is thrown. + A ``RuntimeException`` is thrown if a before middleware does not return a + Response or ``null``. diff --git a/lib/silex/vendor/silex/silex/doc/organizing_controllers.rst b/lib/silex/vendor/silex/silex/doc/organizing_controllers.rst index 5b3878bbf..89fc68674 100644 --- a/lib/silex/vendor/silex/silex/doc/organizing_controllers.rst +++ b/lib/silex/vendor/silex/silex/doc/organizing_controllers.rst @@ -25,6 +25,16 @@ group them logically:: $app->mount('/blog', $blog); $app->mount('/forum', $forum); + // define controllers for an admin + $app->mount('/admin', function ($admin) { + // recursively mount + $admin->mount('/blog', function ($user) { + $user->get('/', function () { + return 'Admin Blog home page'; + }); + }); + }); + .. note:: ``$app['controllers_factory']`` is a factory that returns a new instance @@ -32,7 +42,8 @@ group them logically:: ``mount()`` prefixes all routes with the given prefix and merges them into the main Application. So, ``/`` will map to the main home page, ``/blog/`` to the -blog home page, and ``/forum/`` to the forum home page. +blog home page, ``/forum/`` to the forum home page, and ``/admin/blog/`` to the +admin blog home page. .. caution:: diff --git a/lib/silex/vendor/silex/silex/doc/providers.rst b/lib/silex/vendor/silex/silex/doc/providers.rst index 8dde32da0..c3d049db7 100644 --- a/lib/silex/vendor/silex/silex/doc/providers.rst +++ b/lib/silex/vendor/silex/silex/doc/providers.rst @@ -32,7 +32,7 @@ Conventions ~~~~~~~~~~~ You need to watch out in what order you do certain things when interacting -with providers. Just keep to these rules: +with providers. Just keep these rules in mind: * Overriding existing services must occur **after** the provider is registered. @@ -45,66 +45,104 @@ with providers. Just keep to these rules: *Reason: Providers can set default values for parameters. Just like with services, the provider will overwrite existing values.* -Make sure to stick to this behavior when creating your own providers. - Included providers ~~~~~~~~~~~~~~~~~~ There are a few providers that you get out of the box. All of these are within the ``Silex\Provider`` namespace: +* :doc:`AssetServiceProvider ` +* :doc:`CsrfServiceProvider ` * :doc:`DoctrineServiceProvider ` -* :doc:`MonologServiceProvider ` -* :doc:`SessionServiceProvider ` -* :doc:`SerializerServiceProvider ` -* :doc:`SwiftmailerServiceProvider ` -* :doc:`TwigServiceProvider ` -* :doc:`TranslationServiceProvider ` -* :doc:`UrlGeneratorServiceProvider ` -* :doc:`ValidatorServiceProvider ` -* :doc:`HttpCacheServiceProvider ` * :doc:`FormServiceProvider ` -* :doc:`SecurityServiceProvider ` +* :doc:`HttpCacheServiceProvider ` +* :doc:`HttpFragmentServiceProvider ` +* :doc:`LocaleServiceProvider ` +* :doc:`MonologServiceProvider ` * :doc:`RememberMeServiceProvider ` +* :doc:`SecurityServiceProvider ` +* :doc:`SerializerServiceProvider ` * :doc:`ServiceControllerServiceProvider ` +* :doc:`SessionServiceProvider ` +* :doc:`SwiftmailerServiceProvider ` +* :doc:`TranslationServiceProvider ` +* :doc:`TwigServiceProvider ` +* :doc:`ValidatorServiceProvider ` +* :doc:`VarDumperServiceProvider ` + +.. note:: + + The Silex core team maintains a `WebProfiler + `_ provider that helps debug + code in the development environment thanks to the Symfony web debug toolbar + and the Symfony profiler. Third party providers ~~~~~~~~~~~~~~~~~~~~~ Some service providers are developed by the community. Those third-party providers are listed on `Silex' repository wiki -`_. +`_. You are encouraged to share yours. Creating a provider ~~~~~~~~~~~~~~~~~~~ -Providers must implement the ``Silex\ServiceProviderInterface``:: +Providers must implement the ``Pimple\ServiceProviderInterface``:: interface ServiceProviderInterface { - public function register(Application $app); - - public function boot(Application $app); + public function register(Container $container); } -This is very straight forward, just create a new class that implements the two -methods. In the ``register()`` method, you can define services on the -application which then may make use of other services and parameters. In the -``boot()`` method, you can configure the application, just before it handles a -request. +This is very straight forward, just create a new class that implements the +register method. In the ``register()`` method, you can define services on the +application which then may make use of other services and parameters. + +.. tip:: + + The ``Pimple\ServiceProviderInterface`` belongs to the Pimple package, so + take care to only use the API of ``Pimple\Container`` within your + ``register`` method. Not only is this a good practice due to the way Pimple + and Silex work, but may allow your provider to be used outside of Silex. + +Optionally, your service provider can implement the +``Silex\Api\BootableProviderInterface``. A bootable provider must +implement the ``boot()`` method, with which you can configure the application, just +before it handles a request:: + + interface BootableProviderInterface + { + function boot(Application $app); + } + +Another optional interface, is the ``Silex\Api\EventListenerProviderInterface``. +This interface contains the ``subscribe()`` method, which allows your provider to +subscribe event listener with Silex's EventDispatcher, just before it handles a +request:: + + interface EventListenerProviderInterface + { + function subscribe(Container $app, EventDispatcherInterface $dispatcher); + } Here is an example of such a provider:: namespace Acme; + use Pimple\Container; + use Pimple\ServiceProviderInterface; use Silex\Application; - use Silex\ServiceProviderInterface; + use Silex\Api\BootableProviderInterface; + use Silex\Api\EventListenerProviderInterface; + use Symfony\Component\EventDispatcher\EventDispatcherInterface; + use Symfony\Component\HttpKernel\KernelEvents; + use Symfony\Component\HttpKernel\Event\FilterResponseEvent; - class HelloServiceProvider implements ServiceProviderInterface + class HelloServiceProvider implements ServiceProviderInterface, BootableProviderInterface, EventListenerProviderInterface { - public function register(Application $app) + public function register(Container $app) { $app['hello'] = $app->protect(function ($name) use ($app) { $default = $app['hello.default_name'] ? $app['hello.default_name'] : ''; @@ -116,6 +154,14 @@ Here is an example of such a provider:: public function boot(Application $app) { + // do something + } + + public function subscribe(Container $app, EventDispatcherInterface $dispatcher) + { + $dispatcher->addListener(KernelEvents::REQUEST, function(FilterResponseEvent $event) use ($app) { + // do something + }); } } @@ -125,14 +171,16 @@ given. If the default is also missing, it will use an empty string. You can now use this provider as follows:: + use Symfony\Component\HttpFoundation\Request; + $app = new Silex\Application(); $app->register(new Acme\HelloServiceProvider(), array( 'hello.default_name' => 'Igor', )); - $app->get('/hello', function () use ($app) { - $name = $app['request']->get('name'); + $app->get('/hello', function (Request $request) use ($app) { + $name = $request->get('name'); return $app['hello']($name); }); @@ -161,7 +209,7 @@ All controllers defined by the provider will now be available under the Creating a provider ~~~~~~~~~~~~~~~~~~~ -Providers must implement the ``Silex\ControllerProviderInterface``:: +Providers must implement the ``Silex\Api\ControllerProviderInterface``:: interface ControllerProviderInterface { @@ -173,7 +221,7 @@ Here is an example of such a provider:: namespace Acme; use Silex\Application; - use Silex\ControllerProviderInterface; + use Silex\Api\ControllerProviderInterface; class HelloControllerProvider implements ControllerProviderInterface { @@ -198,7 +246,7 @@ defined (like ``get``, ``post``, ``match``, ...). The ``Application`` class acts in fact as a proxy for these methods. -You can now use this provider as follows:: +You can use this provider as follows:: $app = new Silex\Application(); diff --git a/lib/silex/vendor/silex/silex/doc/providers/asset.rst b/lib/silex/vendor/silex/silex/doc/providers/asset.rst new file mode 100644 index 000000000..72b184da7 --- /dev/null +++ b/lib/silex/vendor/silex/silex/doc/providers/asset.rst @@ -0,0 +1,69 @@ +Asset +===== + +The *AssetServiceProvider* provides a way to manage URL generation and +versioning of web assets such as CSS stylesheets, JavaScript files and image +files. + +Parameters +---------- + +* **assets.version**: Default version for assets. + +* **assets.format_version** (optional): Default format for assets. + +* **assets.base_path**: Default path to prepend to all assets without a package. + +* **assets.named_packages** (optional): Named packages. Keys are the package + names and values the configuration (supported keys are ``version``, + ``version_format``, ``base_urls``, and ``base_path``). + +Services +-------- + +* **assets.packages**: The asset service. + +Registering +----------- + +.. code-block:: php + + $app->register(new Silex\Provider\AssetServiceProvider(), array( + 'assets.version' => 'v1', + 'assets.version_format' => '%s?version=%s', + 'assets.named_packages' => array( + 'css' => array('version' => 'css2', 'base_path' => '/whatever-makes-sense'), + 'images' => array('base_urls' => array('https://img.example.com')), + ), + )); + +.. note:: + + Add the Symfony Asset Component as a dependency: + + .. code-block:: bash + + composer require symfony/asset + + If you want to use assets in your Twig templates, you must also install the + Symfony Twig Bridge: + + .. code-block:: bash + + composer require symfony/twig-bridge + +Usage +----- + +The AssetServiceProvider is mostly useful with the Twig provider: + +.. code-block:: jinja + + {{ asset('/css/foo.png') }} + {{ asset('/css/foo.css', 'css') }} + {{ asset('/img/foo.png', 'images') }} + + {{ asset_version('/css/foo.png') }} + +For more information, check out the `Asset Component documentation +`_. diff --git a/lib/silex/vendor/silex/silex/doc/providers/csrf.rst b/lib/silex/vendor/silex/silex/doc/providers/csrf.rst new file mode 100644 index 000000000..a055c112e --- /dev/null +++ b/lib/silex/vendor/silex/silex/doc/providers/csrf.rst @@ -0,0 +1,53 @@ +CSRF +==== + +The *CsrfServiceProvider* provides a service for building forms in your +application with the Symfony Form component. + +Parameters +---------- + +* none + +Services +-------- + +* **csrf.token_manager**: An instance of an implementation of the + `CsrfTokenManagerInterface + `_, + +Registering +----------- + +.. code-block:: php + + use Silex\Provider\CsrfServiceProvider; + + $app->register(new CsrfServiceProvider()); + +.. note:: + + Add the Symfony's `Security CSRF Component + `_ as a + dependency: + + .. code-block:: bash + + composer require symfony/security-csrf + +Usage +----- + +When the CSRF Service Provider is registered, all forms created via the Form +Service Provider are protected against CSRF by default. + +You can also use the CSRF protection without using the Symfony Form component. +If, for example, you're doing a DELETE action, create a CSRF token to use in +your code:: + + use Symfony\Component\Security\Csrf\CsrfToken; + $csrfToken = $app['csrf.token_manager']->getToken('token_id'); //'TOKEN' + +Then check it:: + + $app['csrf.token_manager']->isTokenValid(new CsrfToken('token_id', 'TOKEN')); diff --git a/lib/silex/vendor/silex/silex/doc/providers/doctrine.rst b/lib/silex/vendor/silex/silex/doc/providers/doctrine.rst index 689053173..0ef167b78 100644 --- a/lib/silex/vendor/silex/silex/doc/providers/doctrine.rst +++ b/lib/silex/vendor/silex/silex/doc/providers/doctrine.rst @@ -1,12 +1,9 @@ -DoctrineServiceProvider -======================= +Doctrine +======== The *DoctrineServiceProvider* provides integration with the `Doctrine DBAL -`_ for easy database access. - -.. note:: - - There is only a Doctrine DBAL. An ORM service is **not** supplied. +`_ for easy database access +(Doctrine ORM integration is **not** supplied). Parameters ---------- @@ -66,8 +63,7 @@ Registering .. note:: - Doctrine DBAL comes with the "fat" Silex archive but not with the regular - one. If you are using Composer, add it as a dependency: + Add the Doctrine DBAL as a dependency: .. code-block:: bash diff --git a/lib/silex/vendor/silex/silex/doc/providers/form.rst b/lib/silex/vendor/silex/silex/doc/providers/form.rst index 62a81e2c9..6818b8582 100644 --- a/lib/silex/vendor/silex/silex/doc/providers/form.rst +++ b/lib/silex/vendor/silex/silex/doc/providers/form.rst @@ -1,16 +1,13 @@ -FormServiceProvider -=================== +Form +==== The *FormServiceProvider* provides a service for building forms in -your application with the Symfony2 Form component. +your application with the Symfony Form component. Parameters ---------- -* **form.secret**: This secret value is used for generating and validating the - CSRF token for a specific page. It is very important for you to set this - value to a static randomly generated value, to prevent hijacking of your - forms. Defaults to ``md5(__DIR__)``. +* none Services -------- @@ -19,12 +16,6 @@ Services `_, that is used to build a form. -* **form.csrf_provider**: An instance of an implementation of the - `CsrfProviderInterface - `_, - defaults to a `DefaultCsrfProvider - `_. - Registering ----------- @@ -38,34 +29,30 @@ Registering If you don't want to create your own form layout, it's fine: a default one will be used. But you will have to register the :doc:`translation provider - ` as the default form layout requires it. + ` as the default form layout requires it:: + + $app->register(new Silex\Provider\TranslationServiceProvider(), array( + 'translator.domains' => array(), + )); If you want to use validation with forms, do not forget to register the :doc:`Validator provider `. .. note:: - The Symfony Form Component and all its dependencies (optional or not) comes - with the "fat" Silex archive but not with the regular one. If you are using - Composer, add it as a dependency: + Add the Symfony Form Component as a dependency: .. code-block:: bash composer require symfony/form If you are going to use the validation extension with forms, you must also - add a dependency to the ``symfony/config`` and ``symfony/translation`` + add a dependency to the ``symfony/validator`` and ``symfony/config`` components: .. code-block:: bash - composer require symfony/validator symfony/config symfony/translation - - The Symfony Security CSRF component is used to protect forms against CSRF attacks: - - .. code-block:: bash - - composer require symfony/security-csrf + composer require symfony/validator symfony/config If you want to use forms in your Twig templates, you can also install the Symfony Twig Bridge. Make sure to install, if you didn't do that already, @@ -73,7 +60,7 @@ Registering .. code-block:: bash - composer require symfony/twig-bridge symfony/translation + composer require symfony/twig-bridge Usage ----- @@ -81,6 +68,10 @@ Usage The FormServiceProvider provides a ``form.factory`` service. Here is a usage example:: + use Symfony\Component\Form\Extension\Core\Type\ChoiceType; + use Symfony\Component\Form\Extension\Core\Type\FormType; + use Symfony\Component\Form\Extension\Core\Type\SubmitType; + $app->match('/form', function (Request $request) use ($app) { // some default data for when the form is displayed the first time $data = array( @@ -88,13 +79,16 @@ example:: 'email' => 'Your email', ); - $form = $app['form.factory']->createBuilder('form', $data) + $form = $app['form.factory']->createBuilder(FormType::class, $data) ->add('name') ->add('email') - ->add('gender', 'choice', array( - 'choices' => array(1 => 'male', 2 => 'female'), + ->add('billing_plan', ChoiceType::class, array( + 'choices' => array('free' => 1, 'small business' => 2, 'corporate' => 3), 'expanded' => true, )) + ->add('submit', SubmitType::class, [ + 'label' => 'Save', + ]) ->getForm(); $form->handleRequest($request); @@ -125,6 +119,10 @@ And here is the ``index.twig`` form template (requires ``symfony/twig-bridge``): If you are using the validator provider, you can also add validation to your form by adding constraints on the fields:: + use Symfony\Component\Form\Extension\Core\Type\ChoiceType; + use Symfony\Component\Form\Extension\Core\Type\FormType; + use Symfony\Component\Form\Extension\Core\Type\SubmitType; + use Symfony\Component\Form\Extension\Core\Type\TextType; use Symfony\Component\Validator\Constraints as Assert; $app->register(new Silex\Provider\ValidatorServiceProvider()); @@ -132,63 +130,87 @@ form by adding constraints on the fields:: 'translator.domains' => array(), )); - $form = $app['form.factory']->createBuilder('form') - ->add('name', 'text', array( + $form = $app['form.factory']->createBuilder(FormType::class) + ->add('name', TextType::class, array( 'constraints' => array(new Assert\NotBlank(), new Assert\Length(array('min' => 5))) )) - ->add('email', 'text', array( + ->add('email', TextType::class, array( 'constraints' => new Assert\Email() )) - ->add('gender', 'choice', array( - 'choices' => array(1 => 'male', 2 => 'female'), + ->add('billing_plan', ChoiceType::class, array( + 'choices' => array('free' => 1, 'small business' => 2, 'corporate' => 3), 'expanded' => true, - 'constraints' => new Assert\Choice(array(1, 2)), + 'constraints' => new Assert\Choice(array(1, 2, 3)), )) + ->add('submit', SubmitType::class, [ + 'label' => 'Save', + ]) ->getForm(); You can register form types by extending ``form.types``:: - $app['form.types'] = $app->share($app->extend('form.types', function ($types) use ($app) { + $app['your.type.service'] = function ($app) { + return new YourServiceFormType(); + }; + $app->extend('form.types', function ($types) use ($app) { $types[] = new YourFormType(); + $types[] = 'your.type.service'; return $types; })); You can register form extensions by extending ``form.extensions``:: - $app['form.extensions'] = $app->share($app->extend('form.extensions', function ($extensions) use ($app) { + $app->extend('form.extensions', function ($extensions) use ($app) { $extensions[] = new YourTopFormExtension(); return $extensions; - })); + }); You can register form type extensions by extending ``form.type.extensions``:: - $app['form.type.extensions'] = $app->share($app->extend('form.type.extensions', function ($extensions) use ($app) { + $app['your.type.extension.service'] = function ($app) { + return new YourServiceFormTypeExtension(); + }; + $app->extend('form.type.extensions', function ($extensions) use ($app) { $extensions[] = new YourFormTypeExtension(); + $extensions[] = 'your.type.extension.service'; return $extensions; - })); + }); You can register form type guessers by extending ``form.type.guessers``:: - $app['form.type.guessers'] = $app->share($app->extend('form.type.guessers', function ($guessers) use ($app) { + $app['your.type.guesser.service'] = function ($app) { + return new YourServiceFormTypeGuesser(); + }; + $app->extend('form.type.guessers', function ($guessers) use ($app) { $guessers[] = new YourFormTypeGuesser(); + $guessers[] = 'your.type.guesser.service'; return $guessers; - })); + }); + +.. warning:: + + CSRF protection is only available and automatically enabled when the + :doc:`CSRF Service Provider ` is registered. Traits ------ ``Silex\Application\FormTrait`` adds the following shortcuts: -* **form**: Creates a FormBuilder instance. +* **form**: Creates a FormBuilderInterface instance. + +* **namedForm**: Creates a FormBuilderInterface instance (named). .. code-block:: php $app->form($data); -For more information, consult the `Symfony2 Forms documentation -`_. + $app->namedForm($name, $data, $options, $type); + +For more information, consult the `Symfony Forms documentation +`_. diff --git a/lib/silex/vendor/silex/silex/doc/providers/http_cache.rst b/lib/silex/vendor/silex/silex/doc/providers/http_cache.rst index c0e0ee559..8bc98f67a 100644 --- a/lib/silex/vendor/silex/silex/doc/providers/http_cache.rst +++ b/lib/silex/vendor/silex/silex/doc/providers/http_cache.rst @@ -1,7 +1,7 @@ -HttpCacheServiceProvider -======================== +HTTP Cache +========== -The *HttpCacheServiceProvider* provides support for the Symfony2 Reverse +The *HttpCacheServiceProvider* provides support for the Symfony Reverse Proxy. Parameters @@ -58,20 +58,16 @@ setting Response HTTP cache headers:: in `Trusting Proxies `_. - If you would be running Varnish in front of your application on the same machine: - - .. code-block:: php + If you would be running Varnish in front of your application on the same machine:: use Symfony\Component\HttpFoundation\Request; Request::setTrustedProxies(array('127.0.0.1', '::1')); $app->run(); -This provider allows you to use the Symfony2 reverse proxy natively with -Silex applications by using the ``http_cache`` service. The Symfony2 reverse proxy -acts much like any other proxy would, so you will want to whitelist it: - -.. code-block:: php +This provider allows you to use the Symfony reverse proxy natively with +Silex applications by using the ``http_cache`` service. The Symfony reverse proxy +acts much like any other proxy would, so you will want to whitelist it:: use Symfony\Component\HttpFoundation\Request; @@ -128,5 +124,5 @@ overall performance:: Finally, check that your Web server does not override your caching strategy. -For more information, consult the `Symfony2 HTTP Cache documentation +For more information, consult the `Symfony HTTP Cache documentation `_. diff --git a/lib/silex/vendor/silex/silex/doc/providers/http_fragment.rst b/lib/silex/vendor/silex/silex/doc/providers/http_fragment.rst index 2550c642b..8e681853a 100644 --- a/lib/silex/vendor/silex/silex/doc/providers/http_fragment.rst +++ b/lib/silex/vendor/silex/silex/doc/providers/http_fragment.rst @@ -1,13 +1,9 @@ -HttpFragmentServiceProvider -=========================== +HTTP Fragment +============= -The *HttpFragmentServiceProvider* provides support for the Symfony2 fragment +The *HttpFragmentServiceProvider* provides support for the Symfony fragment sub-framework, which allows you to embed fragments of HTML in a template. -.. warning:: - - This service provider only work with Symfony 2.4+. - Parameters ---------- diff --git a/lib/silex/vendor/silex/silex/doc/providers/index.rst b/lib/silex/vendor/silex/silex/doc/providers/index.rst index 74d3ebb4d..8c5a17547 100644 --- a/lib/silex/vendor/silex/silex/doc/providers/index.rst +++ b/lib/silex/vendor/silex/silex/doc/providers/index.rst @@ -1,21 +1,24 @@ -Silex -===== +Built-in Service Providers +========================== .. toctree:: :maxdepth: 1 - doctrine + twig + asset monolog session swiftmailer + locale translation - twig - url_generator validator form + csrf http_cache http_fragment security remember_me serializer service_controller + var_dumper + doctrine diff --git a/lib/silex/vendor/silex/silex/doc/providers/locale.rst b/lib/silex/vendor/silex/silex/doc/providers/locale.rst new file mode 100644 index 000000000..8f6cd675f --- /dev/null +++ b/lib/silex/vendor/silex/silex/doc/providers/locale.rst @@ -0,0 +1,24 @@ +Locale +====== + +The *LocaleServiceProvider* manages the locale of an application. + +Parameters +---------- + +* **locale**: The locale of the user. When set before any request handling, it + defines the default locale (``en`` by default). When a request is being + handled, it is automatically set according to the ``_locale`` request + attribute of the current route. + +Services +-------- + +* n/a + +Registering +----------- + +.. code-block:: php + + $app->register(new Silex\Provider\LocaleServiceProvider()); diff --git a/lib/silex/vendor/silex/silex/doc/providers/monolog.rst b/lib/silex/vendor/silex/silex/doc/providers/monolog.rst index 368c42a40..645b71061 100644 --- a/lib/silex/vendor/silex/silex/doc/providers/monolog.rst +++ b/lib/silex/vendor/silex/silex/doc/providers/monolog.rst @@ -1,5 +1,5 @@ -MonologServiceProvider -====================== +Monolog +======= The *MonologServiceProvider* provides a default logging mechanism through Jordi Boggiano's `Monolog `_ library. @@ -12,8 +12,8 @@ Parameters ---------- * **monolog.logfile**: File where logs are written to. -* **monolog.bubble** = (optional) Whether the messages that are handled can bubble up the stack or not. -* **monolog.permission** = (optional) File permissions default (null), nothing change. +* **monolog.bubble**: (optional) Whether the messages that are handled can bubble up the stack or not. +* **monolog.permission**: (optional) File permissions default (null), nothing change. * **monolog.level** (optional): Level of logging, defaults to ``DEBUG``. Must be one of ``Logger::DEBUG``, ``Logger::INFO``, @@ -28,6 +28,17 @@ Parameters * **monolog.name** (optional): Name of the monolog channel, defaults to ``myapp``. +* **monolog.exception.logger_filter** (optional): An anonymous function that + returns an error level for on uncaught exception that should be logged. + +* **monolog.use_error_handler** (optional): Whether errors and uncaught exceptions + should be handled by the Monolog ``ErrorHandler`` class and added to the log. + By default the error handler is enabled unless the application ``debug`` parameter + is set to true. + + Please note that enabling the error handler may silence some errors, + ignoring the PHP ``display_errors`` configuration setting. + Services -------- @@ -35,7 +46,7 @@ Services Example usage:: - $app['monolog']->addDebug('Testing the Monolog logging.'); + $app['monolog']->debug('Testing the Monolog logging.'); * **monolog.listener**: An event listener to log requests, responses and errors. @@ -50,8 +61,7 @@ Registering .. note:: - Monolog comes with the "fat" Silex archive but not with the regular one. - If you are using Composer, add it as a dependency: + Add Monolog as a dependency: .. code-block:: bash @@ -61,15 +71,15 @@ Usage ----- The MonologServiceProvider provides a ``monolog`` service. You can use it to -add log entries for any logging level through ``addDebug()``, ``addInfo()``, -``addWarning()`` and ``addError()``:: +add log entries for any logging level through ``debug()``, ``info()``, +``warning()`` and ``error()``:: use Symfony\Component\HttpFoundation\Response; $app->post('/user', function () use ($app) { // ... - $app['monolog']->addInfo(sprintf("User '%s' registered.", $username)); + $app['monolog']->info(sprintf("User '%s' registered.", $username)); return new Response('', 201); }); @@ -80,15 +90,15 @@ Customization You can configure Monolog (like adding or changing the handlers) before using it by extending the ``monolog`` service:: - $app['monolog'] = $app->share($app->extend('monolog', function($monolog, $app) { + $app->extend('monolog', function($monolog, $app) { $monolog->pushHandler(...); return $monolog; - })); + }); By default, all requests, responses and errors are logged by an event listener registered as a service called `monolog.listener`. You can replace or remove -this service if you want to modify or disable the informations logged. +this service if you want to modify or disable the logged information. Traits ------ diff --git a/lib/silex/vendor/silex/silex/doc/providers/remember_me.rst b/lib/silex/vendor/silex/silex/doc/providers/remember_me.rst index 16b4aa27f..7fdaaabad 100644 --- a/lib/silex/vendor/silex/silex/doc/providers/remember_me.rst +++ b/lib/silex/vendor/silex/silex/doc/providers/remember_me.rst @@ -1,5 +1,5 @@ -RememberMeServiceProvider -========================= +Remember Me +=========== The *RememberMeServiceProvider* adds "Remember-Me" authentication to the *SecurityServiceProvider*. diff --git a/lib/silex/vendor/silex/silex/doc/providers/security.rst b/lib/silex/vendor/silex/silex/doc/providers/security.rst index 3a790dcb7..37ae0504e 100644 --- a/lib/silex/vendor/silex/silex/doc/providers/security.rst +++ b/lib/silex/vendor/silex/silex/doc/providers/security.rst @@ -1,5 +1,5 @@ -SecurityServiceProvider -======================= +Security +======== The *SecurityServiceProvider* manages authentication and authorization for your applications. @@ -10,11 +10,15 @@ Parameters * **security.hide_user_not_found** (optional): Defines whether to hide user not found exception or not. Defaults to ``true``. +* **security.encoder.bcrypt.cost** (optional): Defines BCrypt password encoder cost. Defaults to 13. + Services -------- -* **security**: The main entry point for the security provider. Use it to get - the current user token. +* **security.token_storage**: Gives access to the user token. + +* **security.authorization_checker**: Allows to check authorizations for the + users. * **security.authentication_manager**: An instance of `AuthenticationProviderManager @@ -34,9 +38,17 @@ Services Request object. * **security.encoder_factory**: Defines the encoding strategies for user - passwords (default to use a digest algorithm for all users). + passwords (uses ``security.default_encoder``). -* **security.encoder.digest**: The encoder to use by default for all users. +* **security.default_encoder**: The encoder to use by default for all users (BCrypt). + +* **security.encoder.digest**: Digest password encoder. + +* **security.encoder.bcrypt**: BCrypt password encoder. + +* **security.encoder.pbkdf2**: Pbkdf2 password encoder. + +* **user**: Returns the current user .. note:: @@ -54,32 +66,31 @@ Registering .. note:: - The Symfony Security Component comes with the "fat" Silex archive but not - with the regular one. If you are using Composer, add it as a dependency: + Add the Symfony Security Component as a dependency: .. code-block:: bash composer require symfony/security +.. caution:: + + If you're using a form to authenticate users, you need to enable + ``SessionServiceProvider``. + .. caution:: The security features are only available after the Application has been booted. So, if you want to use it outside of the handling of a request, don't forget to call ``boot()`` first:: - $application->boot(); - -.. caution:: - - If you're using a form to authenticate users, you need to enable - ``SessionServiceProvider``. + $app->boot(); Usage ----- The Symfony Security component is powerful. To learn more about it, read the -`Symfony2 Security documentation -`_. +`Symfony Security documentation +`_. .. tip:: @@ -95,7 +106,7 @@ Accessing the current User The current user information is stored in a token that is accessible via the ``security`` service:: - $token = $app['security']->getToken(); + $token = $app['security.token_storage']->getToken(); If there is no information about the user, the token is ``null``. If the user is known, you can get it with a call to ``getUser()``:: @@ -120,15 +131,29 @@ under ``/admin/``:: 'http' => true, 'users' => array( // raw password is foo - 'admin' => array('ROLE_ADMIN', '5FZ2Z8QIkA7UTZ4BYkoC+GsReLf569mSKDsfods6LYQ8t+a8EW9oaircfMpmaLbPBh4FOBiiFyLfuZmTSUwzZg=='), + 'admin' => array('ROLE_ADMIN', '$2y$10$3i9/lVd8UOFIJ6PAMFt8gu3/r5g0qeCJvoSlLCsvMTythye19F77a'), ), ), ); -The ``pattern`` is a regular expression (it can also be a `RequestMatcher +The ``pattern`` is a regular expression on the URL path; the ``http`` setting +tells the security layer to use HTTP basic authentication and the ``users`` +entry defines valid users. + +If you want to restrict the firewall by more than the URL pattern (like the +HTTP method, the client IP, the hostname, or any Request attributes), use an +instance of a `RequestMatcher `_ -instance); the ``http`` setting tells the security layer to use HTTP basic -authentication and the ``users`` entry defines valid users. +for the ``pattern`` option:: + + use Symfony\Component\HttpFoundation\RequestMatcher; + + $app['security.firewalls'] = array( + 'admin' => array( + 'pattern' => new RequestMatcher('^/admin', 'example.com', 'POST'), + // ... + ), + ); Each user is defined with the following information: @@ -189,7 +214,7 @@ Here is how to secure all URLs under ``/admin/`` with a form:: 'pattern' => '^/admin/', 'form' => array('login_path' => '/login', 'check_path' => '/admin/login_check'), 'users' => array( - 'admin' => array('ROLE_ADMIN', '5FZ2Z8QIkA7UTZ4BYkoC+GsReLf569mSKDsfods6LYQ8t+a8EW9oaircfMpmaLbPBh4FOBiiFyLfuZmTSUwzZg=='), + 'admin' => array('ROLE_ADMIN', '$2y$10$3i9/lVd8UOFIJ6PAMFt8gu3/r5g0qeCJvoSlLCsvMTythye19F77a'), ), ), ); @@ -254,7 +279,7 @@ It's also useful when you want to secure all URLs except the login form:: 'pattern' => '^.*$', 'form' => array('login_path' => '/login', 'check_path' => '/login_check'), 'users' => array( - 'admin' => array('ROLE_ADMIN', '5FZ2Z8QIkA7UTZ4BYkoC+GsReLf569mSKDsfods6LYQ8t+a8EW9oaircfMpmaLbPBh4FOBiiFyLfuZmTSUwzZg=='), + 'admin' => array('ROLE_ADMIN', '$2y$10$3i9/lVd8UOFIJ6PAMFt8gu3/r5g0qeCJvoSlLCsvMTythye19F77a'), ), ), ); @@ -289,7 +314,7 @@ pattern:: 'secured' => array( 'pattern' => '^/admin/', 'form' => array('login_path' => '/login', 'check_path' => '/admin/login_check'), - 'logout' => array('logout_path' => '/admin/logout'), + 'logout' => array('logout_path' => '/admin/logout', 'invalidate_session' => true), // ... ), @@ -327,7 +352,7 @@ Checking User Roles To check if a user is granted some role, use the ``isGranted()`` method on the security context:: - if ($app['security']->isGranted('ROLE_ADMIN')) { + if ($app['security.authorization_checker']->isGranted('ROLE_ADMIN')) { // ... } @@ -442,9 +467,9 @@ The ``users`` setting can be defined as a service that returns an instance of `UserProviderInterface `_:: - 'users' => $app->share(function () use ($app) { + 'users' => function () use ($app) { return new UserProvider($app['db']); - }), + }, Here is a simple example of a user provider, where Doctrine DBAL is used to store the users:: @@ -515,13 +540,13 @@ sample users:: $app['db']->insert('users', array( 'username' => 'fabien', - 'password' => '5FZ2Z8QIkA7UTZ4BYkoC+GsReLf569mSKDsfods6LYQ8t+a8EW9oaircfMpmaLbPBh4FOBiiFyLfuZmTSUwzZg==', + 'password' => '$2y$10$3i9/lVd8UOFIJ6PAMFt8gu3/r5g0qeCJvoSlLCsvMTythye19F77a', 'roles' => 'ROLE_USER' )); $app['db']->insert('users', array( 'username' => 'admin', - 'password' => '5FZ2Z8QIkA7UTZ4BYkoC+GsReLf569mSKDsfods6LYQ8t+a8EW9oaircfMpmaLbPBh4FOBiiFyLfuZmTSUwzZg==', + 'password' => '$2y$10$3i9/lVd8UOFIJ6PAMFt8gu3/r5g0qeCJvoSlLCsvMTythye19F77a', 'roles' => 'ROLE_ADMIN' )); } @@ -535,19 +560,35 @@ sample users:: Defining a custom Encoder ~~~~~~~~~~~~~~~~~~~~~~~~~ -By default, Silex uses the ``sha512`` algorithm to encode passwords. -Additionally, the password is encoded multiple times and converted to base64. -You can change these defaults by overriding the ``security.encoder.digest`` -service:: +By default, Silex uses the ``BCrypt`` algorithm to encode passwords. +Additionally, the password is encoded multiple times. +You can change these defaults by overriding ``security.default_encoder`` +service to return one of the predefined encoders: - use Symfony\Component\Security\Core\Encoder\MessageDigestPasswordEncoder; +* **security.encoder.digest**: Digest password encoder. - $app['security.encoder.digest'] = $app->share(function ($app) { - // use the sha1 algorithm - // don't base64 encode the password - // use only 1 iteration - return new MessageDigestPasswordEncoder('sha1', false, 1); - }); +* **security.encoder.bcrypt**: BCrypt password encoder. + +* **security.encoder.pbkdf2**: Pbkdf2 password encoder. + +.. code-block:: php + + $app['security.default_encoder'] = function ($app) { + return $app['security.encoder.pbkdf2']; + }; + +Or you can define you own, fully customizable encoder:: + + use Symfony\Component\Security\Core\Encoder\PlaintextPasswordEncoder; + + $app['security.default_encoder'] = function ($app) { + // Plain text (e.g. for debugging) + return new PlaintextPasswordEncoder(); + }; + +.. tip:: + + You can change the default BCrypt encoding cost by overriding ``security.encoder.bcrypt.cost`` Defining a custom Authentication Provider ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -560,14 +601,14 @@ use in your configuration:: $app['security.authentication_listener.factory.wsse'] = $app->protect(function ($name, $options) use ($app) { // define the authentication provider object - $app['security.authentication_provider.'.$name.'.wsse'] = $app->share(function () use ($app) { + $app['security.authentication_provider.'.$name.'.wsse'] = function () use ($app) { return new WsseProvider($app['security.user_provider.default'], __DIR__.'/security_cache'); - }); + }; // define the authentication listener object - $app['security.authentication_listener.'.$name.'.wsse'] = $app->share(function () use ($app) { - return new WsseListener($app['security'], $app['security.authentication_manager']); - }); + $app['security.authentication_listener.'.$name.'.wsse'] = function () use ($app) { + return new WsseListener($app['security.token_storage'], $app['security.authentication_manager']); + }; return array( // the authentication provider id @@ -601,6 +642,13 @@ argument of your authentication factory (see above). This example uses the authentication provider classes as described in the Symfony `cookbook`_. + +.. note:: + + The Guard component simplifies the creation of custom authentication + providers. :doc:`How to Create a Custom Authentication System with Guard + ` + Stateless Authentication ~~~~~~~~~~~~~~~~~~~~~~~~ @@ -623,15 +671,11 @@ Traits ``Silex\Application\SecurityTrait`` adds the following shortcuts: -* **user**: Returns the current user. - * **encodePassword**: Encode a given password. .. code-block:: php - $user = $app->user(); - - $encoded = $app->encodePassword($user, 'foo'); + $encoded = $app->encodePassword($app['user'], 'foo'); ``Silex\Route\SecurityTrait`` adds the following methods to the controllers: @@ -643,4 +687,23 @@ Traits // do something but only for admins })->secure('ROLE_ADMIN'); +.. caution:: + + The ``Silex\Route\SecurityTrait`` must be used with a user defined + ``Route`` class, not the application. + + .. code-block:: php + + use Silex\Route; + + class MyRoute extends Route + { + use Route\SecurityTrait; + } + + .. code-block:: php + + $app['route_class'] = 'MyRoute'; + + .. _cookbook: http://symfony.com/doc/current/cookbook/security/custom_authentication_provider.html diff --git a/lib/silex/vendor/silex/silex/doc/providers/serializer.rst b/lib/silex/vendor/silex/silex/doc/providers/serializer.rst index 7020fe22f..be5847de2 100644 --- a/lib/silex/vendor/silex/silex/doc/providers/serializer.rst +++ b/lib/silex/vendor/silex/silex/doc/providers/serializer.rst @@ -1,5 +1,5 @@ -SerializerServiceProvider -=========================== +Serializer +========== The *SerializerServiceProvider* provides a service for serializing objects. @@ -33,10 +33,9 @@ Registering .. note:: - The *SerializerServiceProvider* relies on Symfony's `Serializer Component - `_, - which comes with the "fat" Silex archive but not with the regular - one. If you are using Composer, add it as a dependency: + Add the Symfony's `Serializer Component + `_ as a + dependency: .. code-block:: bash @@ -45,12 +44,11 @@ Registering Usage ----- -The ``SerializerServiceProvider`` provider provides a ``serializer`` service: - -.. code-block:: php +The ``SerializerServiceProvider`` provider provides a ``serializer`` service:: use Silex\Application; use Silex\Provider\SerializerServiceProvider; + use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; $app = new Application(); @@ -58,19 +56,30 @@ The ``SerializerServiceProvider`` provider provides a ``serializer`` service: $app->register(new SerializerServiceProvider()); // only accept content types supported by the serializer via the assert method. - $app->get("/pages/{id}.{_format}", function ($id) use ($app) { + $app->get("/pages/{id}.{_format}", function (Request $request, $id) use ($app) { // assume a page_repository service exists that returns Page objects. The // object returned has getters and setters exposing the state. $page = $app['page_repository']->find($id); - $format = $app['request']->getRequestFormat(); + $format = $request->getRequestFormat(); if (!$page instanceof Page) { $app->abort("No page found for id: $id"); } return new Response($app['serializer']->serialize($page, $format), 200, array( - "Content-Type" => $app['request']->getMimeType($format) + "Content-Type" => $request->getMimeType($format) )); })->assert("_format", "xml|json") ->assert("id", "\d+"); +Using a Cache +------------- + +To use a cache, register a class implementing ``Doctrine\Common\Cache\Cache``:: + + $app->register(new Silex\Provider\SerializerServiceProvider()); + $app['serializer.normalizers'] = function () use ($app) { + return [new \Symfony\Component\Serializer\Normalizer\CustomNormalizer(), + new \Symfony\Component\Serializer\Normalizer\GetSetMethodNormalizer(new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader()), $app['my_custom_cache'])) + ]; + }; diff --git a/lib/silex/vendor/silex/silex/doc/providers/service_controller.rst b/lib/silex/vendor/silex/silex/doc/providers/service_controller.rst index 0d1c700a5..15bca28d1 100644 --- a/lib/silex/vendor/silex/silex/doc/providers/service_controller.rst +++ b/lib/silex/vendor/silex/silex/doc/providers/service_controller.rst @@ -1,5 +1,5 @@ -ServiceControllerServiceProvider -================================ +Service Controllers +=================== As your Silex application grows, you may wish to begin organizing your controllers in a more formal fashion. Silex can use controller classes out of @@ -58,9 +58,9 @@ In this slightly contrived example of a blog API, we're going to change the $app = new Application(); - $app['posts.repository'] = $app->share(function() { + $app['posts.repository'] = function() { return new PostRepository; - }); + }; $app->get('/posts.json', function() use ($app) { return $app->json($app['posts.repository']->findAll()); @@ -109,8 +109,34 @@ followed by a single colon (:), followed by the method name. .. code-block:: php - $app['posts.controller'] = $app->share(function() use ($app) { + $app['posts.controller'] = function() use ($app) { return new PostController($app['posts.repository']); - }); + }; $app->get('/posts.json', "posts.controller:indexJsonAction"); + +In addition to using classes for service controllers, you can define any +callable as a service in the application to be used for a route. + +.. code-block:: php + + namespace Demo\Controller; + + use Demo\Repository\PostRepository; + use Symfony\Component\HttpFoundation\JsonResponse; + + function postIndexJson(PostRepository $repo) { + return function() use ($repo) { + return new JsonResponse($repo->findAll()); + }; + } + +And when defining your route, the code would look like the following: + +.. code-block:: php + + $app['posts.controller'] = function($app) { + return Demo\Controller\postIndexJson($app['posts.repository']); + }; + + $app->get('/posts.json', 'posts.controller'); diff --git a/lib/silex/vendor/silex/silex/doc/providers/session.rst b/lib/silex/vendor/silex/silex/doc/providers/session.rst index 4377c8434..011b69feb 100644 --- a/lib/silex/vendor/silex/silex/doc/providers/session.rst +++ b/lib/silex/vendor/silex/silex/doc/providers/session.rst @@ -1,5 +1,5 @@ -SessionServiceProvider -====================== +Session +======= The *SessionServiceProvider* provides a service for storing data persistently between requests. @@ -38,7 +38,7 @@ Parameters Services -------- -* **session**: An instance of Symfony2's `Session +* **session**: An instance of Symfony's `Session `_. * **session.storage**: A service that is used for persistence of the session @@ -56,17 +56,35 @@ Registering $app->register(new Silex\Provider\SessionServiceProvider()); +Using Handlers +-------------- + +The default session handler is ``NativeFileSessionHandler``. However, there are +multiple handlers available for use by setting ``session.storage.handler`` to +an instance of one of the following handler objects: + +* `LegacyPdoSessionHandler `_ +* `MemcacheSessionHandler `_ +* `MemcachedSessionHandler `_ +* `MongoDbSessionHandler `_ +* `NativeFileSessionHandler `_ +* `NativeSessionHandler `_ +* `NullSessionHandler `_ +* `PdoSessionHandler `_ +* `WriteCheckSessionHandler `_ + Usage ----- The Session provider provides a ``session`` service. Here is an example that authenticates a user and creates a session for them:: + use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; - $app->get('/login', function () use ($app) { - $username = $app['request']->server->get('PHP_AUTH_USER', false); - $password = $app['request']->server->get('PHP_AUTH_PW'); + $app->get('/login', function (Request $request) use ($app) { + $username = $request->server->get('PHP_AUTH_USER', false); + $password = $request->server->get('PHP_AUTH_PW'); if ('igor' === $username && 'password' === $password) { $app['session']->set('user', array('username' => $username)); diff --git a/lib/silex/vendor/silex/silex/doc/providers/swiftmailer.rst b/lib/silex/vendor/silex/silex/doc/providers/swiftmailer.rst index 563bcaa54..9297d665b 100644 --- a/lib/silex/vendor/silex/silex/doc/providers/swiftmailer.rst +++ b/lib/silex/vendor/silex/silex/doc/providers/swiftmailer.rst @@ -1,5 +1,5 @@ -SwiftmailerServiceProvider -========================== +Swiftmailer +=========== The *SwiftmailerServiceProvider* provides a service for sending email through the `Swift Mailer `_ library. @@ -11,7 +11,8 @@ Parameters ---------- * **swiftmailer.use_spool**: A boolean to specify whether or not to use the - memory spool, defaults to true. + memory spool, defaults to true. + * **swiftmailer.options**: An array of options for the default SMTP-based configuration. @@ -35,6 +36,27 @@ Parameters 'auth_mode' => null ); +* **swiftmailer.sender_address**: If set, all messages will be delivered with + this address as the "return path" address. + +* **swiftmailer.delivery_addresses**: If not empty, all email messages will be + sent to those addresses instead of being sent to their actual recipients. This + is often useful when developing. + +* **swiftmailer.delivery_whitelist**: Used in combination with + ``delivery_addresses``. If set, emails matching any of these patterns will be + delivered like normal, as well as being sent to ``delivery_addresses``. + +* **swiftmailer.plugins**: Array of SwiftMailer plugins. + + Example usage:: + + $app['swiftmailer.plugins'] = function ($app) { + return array( + new \Swift_Plugins_PopBeforeSmtpPlugin('pop3.example.com'), + ); + }; + Services -------- @@ -70,8 +92,7 @@ Registering .. note:: - SwiftMailer comes with the "fat" Silex archive but not with the regular - one. If you are using Composer, add it as a dependency: + Add SwiftMailer as a dependency: .. code-block:: bash @@ -82,9 +103,9 @@ Usage The Swiftmailer provider provides a ``mailer`` service:: - $app->post('/feedback', function () use ($app) { - $request = $app['request']; + use Symfony\Component\HttpFoundation\Request; + $app->post('/feedback', function (Request $request) use ($app) { $message = \Swift_Message::newInstance() ->setSubject('[YourSite] Feedback') ->setFrom(array('noreply@yoursite.com')) diff --git a/lib/silex/vendor/silex/silex/doc/providers/translation.rst b/lib/silex/vendor/silex/silex/doc/providers/translation.rst index 4eac30e11..145fc18c6 100644 --- a/lib/silex/vendor/silex/silex/doc/providers/translation.rst +++ b/lib/silex/vendor/silex/silex/doc/providers/translation.rst @@ -1,5 +1,5 @@ -TranslationServiceProvider -========================== +Translation +=========== The *TranslationServiceProvider* provides a service for translating your application into different languages. @@ -37,15 +37,14 @@ Registering .. code-block:: php + $app->register(new Silex\Provider\LocaleServiceProvider()); $app->register(new Silex\Provider\TranslationServiceProvider(), array( 'locale_fallbacks' => array('en'), )); .. note:: - The Symfony Translation Component comes with the "fat" Silex archive but - not with the regular one. If you are using Composer, add it as a - dependency: + Add the Symfony Translation Component as a dependency: .. code-block:: bash @@ -93,6 +92,22 @@ The above example will result in following routes: * ``/it/hello/igor`` will return ``Hello igor`` (because of the fallback). +Using Resources +--------------- + +When translations are stored in a file, you can load them as follows:: + + $app = new Application(); + + $app->register(new TranslationServiceProvider()); + $app->extend('translator.resources', function ($resources, $app) { + $resources = array_merge($resources, array( + array('array', array('This value should be a valid number.' => 'Cette valeur doit être un nombre.'), 'fr', 'validators'), + )); + + return $resources; + }); + Traits ------ @@ -118,7 +133,7 @@ YAML-based language files Having your translations in PHP files can be inconvenient. This recipe will show you how to load translations from external YAML files. -First, add the Symfony2 ``Config`` and ``Yaml`` components as dependencies: +First, add the Symfony ``Config`` and ``Yaml`` components as dependencies: .. code-block:: bash @@ -137,7 +152,7 @@ translation files:: use Symfony\Component\Translation\Loader\YamlFileLoader; - $app['translator'] = $app->share($app->extend('translator', function($translator, $app) { + $app->extend('translator', function($translator, $app) { $translator->addLoader('yaml', new YamlFileLoader()); $translator->addResource('yaml', __DIR__.'/locales/en.yml', 'en'); @@ -145,13 +160,13 @@ translation files:: $translator->addResource('yaml', __DIR__.'/locales/fr.yml', 'fr'); return $translator; - })); + }); XLIFF-based language files ~~~~~~~~~~~~~~~~~~~~~~~~~~ Just as you would do with YAML translation files, you first need to add the -Symfony2 ``Config`` component as a dependency (see above for details). +Symfony ``Config`` component as a dependency (see above for details). Then, similarly, create XLIFF files in your locales directory and add them to the translator:: @@ -168,15 +183,8 @@ Accessing translations in Twig templates ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Once loaded, the translation service provider is available from within Twig -templates: - -.. code-block:: jinja - - {{ app.translator.trans('translation_key') }} - -Moreover, when using the Twig bridge provided by Symfony (see -:doc:`TwigServiceProvider `), you will be allowed to translate -strings in the Twig way: +templates when using the Twig bridge provided by Symfony (see +:doc:`TwigServiceProvider `): .. code-block:: jinja diff --git a/lib/silex/vendor/silex/silex/doc/providers/twig.rst b/lib/silex/vendor/silex/silex/doc/providers/twig.rst index a4b5afa14..b713e1a35 100644 --- a/lib/silex/vendor/silex/silex/doc/providers/twig.rst +++ b/lib/silex/vendor/silex/silex/doc/providers/twig.rst @@ -1,5 +1,5 @@ -TwigServiceProvider -=================== +Twig +==== The *TwigServiceProvider* provides integration with the `Twig `_ template engine. @@ -18,7 +18,33 @@ Parameters for more information. * **twig.form.templates** (optional): An array of templates used to render - forms (only available when the ``FormServiceProvider`` is enabled). + forms (only available when the ``FormServiceProvider`` is enabled). The + default theme is ``form_div_layout.html.twig``, but you can use the other + built-in themes: ``form_table_layout.html.twig``, + ``bootstrap_3_layout.html.twig``, and + ``bootstrap_3_horizontal_layout.html.twig``. + +* **twig.date.format** (optional): Default format used by the ``date`` + filter. The format string must conform to the format accepted by + `date() `_. + +* **twig.date.interval_format** (optional): Default format used by the + ``date`` filter when the filtered data is of type `DateInterval `_. + The format string must conform to the format accepted by + `DateInterval::format() `_. + +* **twig.date.timezone** (optional): Default timezone used when formatting + dates. If set to ``null`` the timezone returned by `date_default_timezone_get() `_ + is used. + +* **twig.number_format.decimals** (optional): Default number of decimals + displayed by the ``number_format`` filter. + +* **twig.number_format.decimal_point** (optional): Default separator for + the decimal point used by the ``number_format`` filter. + +* **twig.number_format.thousands_separator** (optional): Default thousands + separator used by the ``number_format`` filter. Services -------- @@ -41,53 +67,16 @@ Registering .. note:: - Twig comes with the "fat" Silex archive but not with the regular one. If - you are using Composer, add it as a dependency: + Add Twig as a dependency: .. code-block:: bash composer require twig/twig -Symfony2 Components Integration -------------------------------- - -Symfony provides a Twig bridge that provides additional integration between -some Symfony2 components and Twig. Add it as a dependency: - -.. code-block:: bash - - composer require symfony/twig-bridge - -When present, the ``TwigServiceProvider`` will provide you with the following -additional capabilities: - -* **UrlGeneratorServiceProvider**: If you are using the - ``UrlGeneratorServiceProvider``, you will have access to the ``path()`` and - ``url()`` functions. You can find more information in the `Symfony2 Routing - documentation - `_. - -* **TranslationServiceProvider**: If you are using the - ``TranslationServiceProvider``, you will get the ``trans()`` and - ``transchoice()`` functions for translation in Twig templates. You can find - more information in the `Symfony2 Translation documentation - `_. - -* **FormServiceProvider**: If you are using the ``FormServiceProvider``, you - will get a set of helpers for working with forms in templates. You can find - more information in the `Symfony2 Forms reference - `_. - -* **SecurityServiceProvider**: If you are using the - ``SecurityServiceProvider``, you will have access to the ``is_granted()`` - function in templates. You can find more information in the `Symfony2 - Security documentation - `_. - Usage ----- -The Twig provider provides a ``twig`` service:: +The Twig provider provides a ``twig`` service that can render templates:: $app->get('/hello/{name}', function ($name) use ($app) { return $app['twig']->render('hello.twig', array( @@ -95,31 +84,110 @@ The Twig provider provides a ``twig`` service:: )); }); -This will render a file named ``views/hello.twig``. +Symfony Components Integration +------------------------------ -In any Twig template, the ``app`` variable refers to the Application object. -So you can access any service from within your view. For example to access -``$app['request']->getHost()``, just put this in your template: +Symfony provides a Twig bridge that provides additional integration between +some Symfony components and Twig. Add it as a dependency: + +.. code-block:: bash + + composer require symfony/twig-bridge + +When present, the ``TwigServiceProvider`` will provide you with the following +additional capabilities. + +* Access to the ``path()`` and ``url()`` functions. You can find more + information in the `Symfony Routing documentation + `_: + + .. code-block:: jinja + + {{ path('homepage') }} + {{ url('homepage') }} {# generates the absolute url http://example.org/ #} + {{ path('hello', {name: 'Fabien'}) }} + {{ url('hello', {name: 'Fabien'}) }} {# generates the absolute url http://example.org/hello/Fabien #} + +* Access to the ``absolute_url()`` and ``relative_path()`` Twig functions. + +Translations Support +~~~~~~~~~~~~~~~~~~~~ + +If you are using the ``TranslationServiceProvider``, you will get the +``trans()`` and ``transchoice()`` functions for translation in Twig templates. +You can find more information in the `Symfony Translation documentation +`_. + +Form Support +~~~~~~~~~~~~ + +If you are using the ``FormServiceProvider``, you will get a set of helpers for +working with forms in templates. You can find more information in the `Symfony +Forms reference +`_. + +Security Support +~~~~~~~~~~~~~~~~ + +If you are using the ``SecurityServiceProvider``, you will have access to the +``is_granted()`` function in templates. You can find more information in the +`Symfony Security documentation +`_. + +Web Link Support +~~~~~~~~~~~~~~~~ + +If you are using the ``symfony/web-link`` component, you will have access to the +``preload()``, ``prefetch()``, ``prerender()``, ``dns_prefetch()``, +``preconnect()`` and ``link()`` functions in templates. You can find more +information in the `Symfony WebLink documentation +`_. + +Global Variable +~~~~~~~~~~~~~~~ + +When the Twig bridge is available, the ``global`` variable refers to an +instance of `AppVariable `_. +It gives access to the following methods: .. code-block:: jinja - {{ app.request.host }} + {# The current Request #} + {{ global.request }} + + {# The current User (when security is enabled) #} + {{ global.user }} + + {# The current Session #} + {{ global.session }} + + {# The debug flag #} + {{ global.debug }} + +Rendering a Controller +~~~~~~~~~~~~~~~~~~~~~~ A ``render`` function is also registered to help you render another controller -from a template: +from a template (available when the :doc:`HttpFragment Service Provider +` is registered): .. code-block:: jinja - {{ render(app.request.baseUrl ~ '/sidebar') }} - - {# or if you are also using the UrlGeneratorServiceProvider #} {{ render(url('sidebar')) }} + {# or you can reference a controller directly without defining a route for it #} + {{ render(controller(controller)) }} + .. note:: You must prepend the ``app.request.baseUrl`` to render calls to ensure that the render works when deployed into a sub-directory of the docroot. +.. note:: + + Read the Twig `reference`_ for Symfony document to learn more about the + various Twig functions. + Traits ------ @@ -144,18 +212,26 @@ Traits return $app->render('index.html', ['name' => 'Fabien'], new StreamedResponse()); +* **renderView**: Renders a view with the given parameters and returns a string. + +.. code-block:: php + + $content = $app->renderView('index.html', ['name' => 'Fabien']); + Customization ------------- You can configure the Twig environment before using it by extending the ``twig`` service:: - $app['twig'] = $app->share($app->extend('twig', function($twig, $app) { + $app->extend('twig', function($twig, $app) { $twig->addGlobal('pi', 3.14); $twig->addFilter('levenshtein', new \Twig_Filter_Function('levenshtein')); return $twig; - })); + }); For more information, check out the `official Twig documentation `_. + +.. _reference: https://symfony.com/doc/current/reference/twig_reference.html#controller diff --git a/lib/silex/vendor/silex/silex/doc/providers/validator.rst b/lib/silex/vendor/silex/silex/doc/providers/validator.rst index 6cbd1f434..bd4e99858 100644 --- a/lib/silex/vendor/silex/silex/doc/providers/validator.rst +++ b/lib/silex/vendor/silex/silex/doc/providers/validator.rst @@ -1,5 +1,5 @@ -ValidatorServiceProvider -======================== +Validator +========= The *ValidatorServiceProvider* provides a service for validating data. It is most useful when used with the *FormServiceProvider*, but can also be used @@ -8,13 +8,14 @@ standalone. Parameters ---------- -none +* **validator.validator_service_ids**: An array of service names representing + validators. Services -------- * **validator**: An instance of `Validator - `_. + `_. * **validator.mapping.class_metadata_factory**: Factory for metadata loaders, which can read validation constraint information from classes. Defaults to @@ -24,10 +25,6 @@ Services data class, which takes a ClassMetadata argument. Then you can set constraints on this ClassMetadata instance. -* **validator.validator_factory**: Factory for ConstraintValidators. Defaults - to a standard ``ConstraintValidatorFactory``. Mostly used internally by the - Validator. - Registering ----------- @@ -37,8 +34,7 @@ Registering .. note:: - The Symfony Validator Component comes with the "fat" Silex archive but not - with the regular one. If you are using Composer, add it as a dependency: + Add the Symfony Validator Component as a dependency: .. code-block:: bash @@ -52,13 +48,13 @@ The Validator provider provides a ``validator`` service. Validating Values ~~~~~~~~~~~~~~~~~ -You can validate values directly using the ``validateValue`` validator +You can validate values directly using the ``validate`` validator method:: use Symfony\Component\Validator\Constraints as Assert; $app->get('/validate/{email}', function ($email) use ($app) { - $errors = $app['validator']->validateValue($email, new Assert\Email()); + $errors = $app['validator']->validate($email, new Assert\Email()); if (count($errors) > 0) { return (string) $errors; @@ -75,18 +71,6 @@ collection of constraints:: use Symfony\Component\Validator\Constraints as Assert; - class Book - { - public $title; - public $author; - } - - class Author - { - public $first_name; - public $last_name; - } - $book = array( 'title' => 'My Book', 'author' => array( @@ -102,7 +86,7 @@ collection of constraints:: 'last_name' => new Assert\Length(array('min' => 10)), )), )); - $errors = $app['validator']->validateValue($book, $constraint); + $errors = $app['validator']->validate($book, $constraint); if (count($errors) > 0) { foreach ($errors as $error) { @@ -120,6 +104,18 @@ the class properties and getters, and then call the ``validate`` method:: use Symfony\Component\Validator\Constraints as Assert; + class Book + { + public $title; + public $author; + } + + class Author + { + public $first_name; + public $last_name; + } + $author = new Author(); $author->first_name = 'Fabien'; $author->last_name = 'Potencier'; @@ -217,5 +213,5 @@ provider and register the messages under the ``validators`` domain:: ), ); -For more information, consult the `Symfony2 Validation documentation +For more information, consult the `Symfony Validation documentation `_. diff --git a/lib/silex/vendor/silex/silex/doc/providers/var_dumper.rst b/lib/silex/vendor/silex/silex/doc/providers/var_dumper.rst new file mode 100644 index 000000000..ea4dd19a4 --- /dev/null +++ b/lib/silex/vendor/silex/silex/doc/providers/var_dumper.rst @@ -0,0 +1,44 @@ +Var Dumper +========== + +The *VarDumperServiceProvider* provides a mechanism that allows exploring then +dumping any PHP variable. + +Parameters +---------- + +* **var_dumper.dump_destination**: A stream URL where dumps should be written + to (defaults to ``null``). + +Services +-------- + +* n/a + +Registering +----------- + +.. code-block:: php + + $app->register(new Silex\Provider\VarDumperServiceProvider()); + +.. note:: + + Add the Symfony VarDumper Component as a dependency: + + .. code-block:: bash + + composer require symfony/var-dumper + +Usage +----- + +Adding the VarDumper component as a Composer dependency gives you access to the +``dump()`` PHP function anywhere in your code. + +If you are using Twig, it also provides a ``dump()`` Twig function and a +``dump`` Twig tag. + +The VarDumperServiceProvider is also useful when used with the Silex +WebProfiler as the dumps are made available in the web debug toolbar and in the +web profiler. diff --git a/lib/silex/vendor/silex/silex/doc/services.rst b/lib/silex/vendor/silex/silex/doc/services.rst index ec8a88c3d..0b34ad35e 100644 --- a/lib/silex/vendor/silex/silex/doc/services.rst +++ b/lib/silex/vendor/silex/silex/doc/services.rst @@ -1,9 +1,9 @@ Services ======== -Silex is not only a microframework. It is also a micro service container. It -does this by extending `Pimple `_ which provides -service goodness in just 44 NCLOC. +Silex is not only a framework, it is also a service container. It does this by +extending `Pimple `_ which provides a very simple +service container. Dependency Injection -------------------- @@ -43,30 +43,20 @@ passed to the constructor. This means you can create several independent instances with different base paths. Of course dependencies do not have to be simple strings. More often they are in fact other services. -Container -~~~~~~~~~ - -A DIC or service container is responsible for creating and storing services. -It can recursively create dependencies of the requested services and inject -them. It does so lazily, which means a service is only created when you -actually need it. - -Most containers are quite complex and are configured through XML or YAML -files. - -Pimple is different. +A service container is responsible for creating and storing services. It can +recursively create dependencies of the requested services and inject them. It +does so lazily, which means a service is only created when you actually need it. Pimple ------ -Pimple is probably the simplest service container out there. It makes strong -use of closures and implements the ArrayAccess interface. +Pimple makes strong use of closures and implements the ArrayAccess interface. We will start off by creating a new instance of Pimple -- and because -``Silex\Application`` extends ``Pimple`` all of this applies to Silex as -well:: +``Silex\Application`` extends ``Pimple\Container`` all of this applies to Silex +as well:: - $container = new Pimple(); + $container = new Pimple\Container(); or:: @@ -80,8 +70,7 @@ the container:: $app['some_parameter'] = 'value'; -The array key can be anything, by convention periods are used for -namespacing:: +The array key can be any value. By convention dots are used for namespacing:: $app['asset.host'] = 'http://cdn.mysite.com/'; @@ -104,21 +93,21 @@ And to retrieve the service, use:: $service = $app['some_service']; -Every time you call ``$app['some_service']``, a new instance of the service is -created. +On first invocation, this will create the service; the same instance will then +be returned on any subsequent access. -Shared services -~~~~~~~~~~~~~~~ +Factory services +~~~~~~~~~~~~~~~~ -You may want to use the same instance of a service across all of your code. In -order to do that you can make a *shared* service:: +If you want a different instance to be returned for each service access, wrap +the service definition with the ``factory()`` method:: - $app['some_service'] = $app->share(function () { + $app['some_service'] = $app->factory(function () { return new Service(); }); -This will create the service on first invocation, and then return the existing -instance on any subsequent access. +Every time you call ``$app['some_service']``, a new instance of the service is +created. Access container from closure ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -139,17 +128,13 @@ options. The dependency is only created when ``some_service`` is accessed, and it is possible to replace either of the dependencies by simply overriding those definitions. -.. note:: - - This also works for shared services. - Going back to our initial example, here's how we could use the container to manage its dependencies:: $app['user.persist_path'] = '/tmp/users'; - $app['user.persister'] = $app->share(function ($app) { + $app['user.persister'] = function ($app) { return new JsonUserPersister($app['user.persist_path']); - }); + }; Protected closures @@ -174,37 +159,48 @@ using the ``protect`` method:: // calling it now echo $add(2, 3); -Note that protected closures do not get access to the container. +Note that the container is not provided as an argument to protected closures. +However, you can inject it via `use($app)`:: + + $app['closure_parameter'] = $app->protect(function ($a, $b) use ($app) { + // ... + }); Core services ------------- -Silex defines a range of services which can be used or replaced. You probably -don't want to mess with most of them. +Silex defines a range of services. -* **request**: Contains the current request object, which is an instance of - `Request - `_. +* **request_stack**: Controls the lifecycle of requests, an instance of + `RequestStack `_. It gives you access to ``GET``, ``POST`` parameters and lots more! Example usage:: - $id = $app['request']->get('id'); + $id = $app['request_stack']->getCurrentRequest()->get('id'); - This is only available when a request is being served, you can only access - it from within a controller, an application before/after middlewares, or an - error handler. + A request is only available when a request is being served; you can only + access it from within a controller, an application before/after middlewares, + or an error handler. * **routes**: The `RouteCollection `_ that is used internally. You can add, modify, read routes. +* **url_generator**: An instance of `UrlGenerator + `_, + using the `RouteCollection + `_ + that is provided through the ``routes`` service. It has a ``generate`` + method, which takes the route name as an argument, followed by an array of + route parameters. + * **controllers**: The ``Silex\ControllerCollection`` that is used internally. - Check the *Internals* chapter for more information. + Check the :doc:`Internals chapter ` for more information. * **dispatcher**: The `EventDispatcher `_ - that is used internally. It is the core of the Symfony2 system and is used + that is used internally. It is the core of the Symfony system and is used quite a bit by Silex. * **resolver**: The `ControllerResolver @@ -214,28 +210,35 @@ don't want to mess with most of them. * **kernel**: The `HttpKernel `_ - that is used internally. The HttpKernel is the heart of Symfony2, it takes a + that is used internally. The HttpKernel is the heart of Symfony, it takes a Request as input and returns a Response as output. * **request_context**: The request context is a simplified representation of - the request that is used by the Router and the UrlGenerator. + the request that is used by the router and the URL generator. * **exception_handler**: The Exception handler is the default handler that is - used when you don't register one via the ``error()`` method or if your handler - does not return a Response. Disable it with - ``$app['exception_handler']->disable()``. + used when you don't register one via the ``error()`` method or if your + handler does not return a Response. Disable it with + ``unset($app['exception_handler'])``. -* **logger**: A ``Psr\Log\LoggerInterface`` instance. By default, logging is +* **logger**: A `LoggerInterface `_ instance. By default, logging is disabled as the value is set to ``null``. To enable logging you can either use - the ``MonologServiceProvider`` or define your own ``logger`` service that + the :doc:`MonologServiceProvider ` or define your own ``logger`` service that conforms to the PSR logger interface. - In versions of Silex before 1.1 this must be a - ``Symfony\Component\HttpKernel\Log\LoggerInterface``. +Core traits +----------- -.. note:: +* ``Silex\Application\UrlGeneratorTrait`` adds the following shortcuts: - All of these Silex core services are shared. + * **path**: Generates a path. + + * **url**: Generates an absolute URL. + + .. code-block:: php + + $app->path('homepage'); + $app->url('homepage'); Core parameters --------------- @@ -246,7 +249,7 @@ Core parameters Defaults to 80. - This parameter can be used by the ``UrlGeneratorProvider``. + This parameter can be used when generating URLs. * **request.https_port** (optional): Allows you to override the default port for HTTPS URLs. If the current request is HTTPS, it will always use the @@ -254,12 +257,7 @@ Core parameters Defaults to 443. - This parameter can be used by the ``UrlGeneratorProvider``. - -* **locale** (optional): The locale of the user. When set before any request - handling, it defines the default locale (``en`` by default). When a request - is being handled, it is automatically set according to the ``_locale`` - request attribute of the current route. + This parameter can be used when generating URLs. * **debug** (optional): Returns whether or not the application is running in debug mode. diff --git a/lib/silex/vendor/silex/silex/doc/testing.rst b/lib/silex/vendor/silex/silex/doc/testing.rst index edbf7e74a..17f5f571c 100644 --- a/lib/silex/vendor/silex/silex/doc/testing.rst +++ b/lib/silex/vendor/silex/silex/doc/testing.rst @@ -1,34 +1,33 @@ Testing ======= -Because Silex is built on top of Symfony2, it is very easy to write functional +Because Silex is built on top of Symfony, it is very easy to write functional tests for your application. Functional tests are automated software tests that -ensure that your code is working correctly. They go through the user -interface, using a fake browser, and mimic the actions a user would do. +ensure that your code is working correctly. They go through the user interface, +using a fake browser, and mimic the actions a user would do. Why --- -If you are not familiar with software tests, you may be wondering why you -would need this. Every time you make a change to your application, you have to -test it. This means going through all the pages and making sure they are still +If you are not familiar with software tests, you may be wondering why you would +need this. Every time you make a change to your application, you have to test +it. This means going through all the pages and making sure they are still working. Functional tests save you a lot of time, because they enable you to test your application in usually under a second by running a single command. For more information on functional testing, unit testing, and automated software tests in general, check out `PHPUnit -`_ and `Bulat Shakirzyanov's -talk on Clean Code -`_. +`_ and `Bulat Shakirzyanov's talk +on Clean Code `_. PHPUnit ------- `PHPUnit `_ is the de-facto -standard testing framework for PHP. It was built for writing unit tests, but -it can be used for functional tests too. You write tests by creating a new -class, that extends the ``PHPUnit_Framework_TestCase``. Your test cases are -methods prefixed with ``test``:: +standard testing framework for PHP. It was built for writing unit tests, but it +can be used for functional tests too. You write tests by creating a new class, +that extends the ``PHPUnit_Framework_TestCase``. Your test cases are methods +prefixed with ``test``:: class ContactFormTest extends \PHPUnit_Framework_TestCase { @@ -60,7 +59,7 @@ section of the PHPUnit documentation. WebTestCase ----------- -Symfony2 provides a WebTestCase class that can be used to write functional +Symfony provides a WebTestCase class that can be used to write functional tests. The Silex version of this class is ``Silex\WebTestCase``, and you can use it by making your test extend it:: @@ -71,9 +70,14 @@ use it by making your test extend it:: ... } +.. caution:: + + If you need to override the ``setUp()`` method, don't forget to call the + parent (``parent::setUp()``) to call the Silex default setup. + .. note:: - If you want to use the Symfony2 ``WebTestCase`` class you will need to + If you want to use the Symfony ``WebTestCase`` class you will need to explicitly install its dependencies for your project: .. code-block:: bash @@ -81,10 +85,11 @@ use it by making your test extend it:: composer require --dev symfony/browser-kit symfony/css-selector For your WebTestCase, you will have to implement a ``createApplication`` -method, which returns your application. It will probably look like this:: +method, which returns your application instance:: public function createApplication() { + // app.php must return an Application instance return require __DIR__.'/path/to/app.php'; } @@ -93,8 +98,8 @@ executed before every test. .. tip:: - By default, the application behaves in the same way as when using it from - a browser. But when an error occurs, it is sometimes easier to get raw + By default, the application behaves in the same way as when using it from a + browser. But when an error occurs, it is sometimes easier to get raw exceptions instead of HTML pages. It is rather simple if you tweak the application configuration in the ``createApplication()`` method like follows:: @@ -103,7 +108,7 @@ executed before every test. { $app = require __DIR__.'/path/to/app.php'; $app['debug'] = true; - $app['exception_handler']->disable(); + unset($app['exception_handler']); return $app; } @@ -122,9 +127,8 @@ executed before every test. // ... } -The WebTestCase provides a ``createClient`` method. A client acts as a -browser, and allows you to interact with your application. Here's how it -works:: +The WebTestCase provides a ``createClient`` method. A client acts as a browser, +and allows you to interact with your application. Here's how it works:: public function testInitialPage() { @@ -143,7 +147,7 @@ There are several things going on here. You have both a ``Client`` and a You can also access the application through ``$this->app``. Client ------- +~~~~~~ The client represents a browser. It holds your browsing history, cookies and more. The ``request`` method allows you to make a request to a page on your @@ -152,11 +156,11 @@ application. .. note:: You can find some documentation for it in `the client section of the - testing chapter of the Symfony2 documentation + testing chapter of the Symfony documentation `_. Crawler -------- +~~~~~~~ The crawler allows you to inspect the content of a page. You can filter it using CSS expressions and lots more. @@ -164,7 +168,7 @@ using CSS expressions and lots more. .. note:: You can find some documentation for it in `the crawler section of the testing - chapter of the Symfony2 documentation + chapter of the Symfony documentation `_. Configuration @@ -178,7 +182,8 @@ look like this: .. code-block:: xml - -You can also configure a bootstrap file for autoloading and whitelisting for -code coverage reports. - Your ``tests/YourApp/Tests/YourTest.php`` should look like this:: namespace YourApp\Tests; @@ -217,4 +219,4 @@ Your ``tests/YourApp/Tests/YourTest.php`` should look like this:: } } -Now, when running ``phpunit`` on the command line, your tests should run. +Now, when running ``phpunit`` on the command line, tests should run. diff --git a/lib/silex/vendor/silex/silex/doc/usage.rst b/lib/silex/vendor/silex/silex/doc/usage.rst index 727390441..724254cef 100644 --- a/lib/silex/vendor/silex/silex/doc/usage.rst +++ b/lib/silex/vendor/silex/silex/doc/usage.rst @@ -1,28 +1,26 @@ Usage ===== -This chapter describes how to use Silex. - Installation ------------ -If you want to get started fast, `download`_ Silex as an archive and extract -it, you should have the following directory structure: +If you want to get started fast, use the `Silex Skeleton`_: -.. code-block:: text +.. code-block:: bash - ├── composer.json - ├── composer.lock - ├── vendor - │ └── ... - └── web - └── index.php + composer create-project fabpot/silex-skeleton path/to/install "~2.0" If you want more flexibility, use Composer_ instead: .. code-block:: bash - composer require silex/silex:~1.2 + composer require silex/silex:~2.0 + +Web Server +---------- + +All examples in the documentation rely on a well-configured web server; read +the :doc:`webserver documentation` to check yours. Bootstrap --------- @@ -40,9 +38,6 @@ definitions, call the ``run`` method on your application:: $app->run(); -Then, you have to configure your web server (read the -:doc:`dedicated chapter ` for more information). - .. tip:: When developing a website, you might want to turn on the debug mode to @@ -65,18 +60,15 @@ Routing ------- In Silex you define a route and the controller that is called when that -route is matched. - -A route pattern consists of: +route is matched. A route pattern consists of: * *Pattern*: The route pattern defines a path that points to a resource. The pattern can include variable parts and you are able to set RegExp requirements for them. * *Method*: One of the following HTTP methods: ``GET``, ``POST``, ``PUT``, - ``DELETE`` or ``PATCH``. This describes the interaction with the resource. - Commonly only ``GET`` and ``POST`` are used, but it is possible to use the - others as well. + ``DELETE``, ``PATCH``, or ``OPTIONS``. This describes the interaction with + the resource. The controller is defined using a closure like this:: @@ -84,17 +76,6 @@ The controller is defined using a closure like this:: // ... do something } -Closures are anonymous functions that may import state from outside of their -definition. This is different from globals, because the outer state does not -have to be global. For instance, you could define a closure in a function and -import local variables of that function. - -.. note:: - - Closures that do not import scope are referred to as lambdas. Because all - anonymous functions are instances of the ``Closure`` class in PHP, the - documentation will not make a distinction here. - The return value of the closure becomes the content of the page. Example GET Route @@ -123,8 +104,8 @@ Here is an example definition of a ``GET`` route:: Visiting ``/blog`` will return a list of blog post titles. The ``use`` statement means something different in this context. It tells the closure to -import the ``$blogPosts`` variable from the outer scope. This allows you to -use it from within the closure. +import the ``$blogPosts`` variable from the outer scope. This allows you to use +it from within the closure. Dynamic Routing ~~~~~~~~~~~~~~~ @@ -186,13 +167,13 @@ This allows setting an HTTP status code, in this case it is set to .. note:: Silex always uses a ``Response`` internally, it converts strings to - responses with status code ``200 Ok``. + responses with status code ``200``. Other methods ~~~~~~~~~~~~~ You can create controllers for most HTTP methods. Just call one of these -methods on your application: ``get``, ``post``, ``put``, ``delete``:: +methods on your application: ``get``, ``post``, ``put``, ``delete``, ``patch``, ``options``:: $app->put('/blog/{id}', function ($id) { // ... @@ -220,8 +201,7 @@ methods on your application: ``get``, ``post``, ``put``, ``delete``:: - If you are using Symfony Components 2.2+, you will need to explicitly - enable this method override:: + You need to explicitly enable this method override:: use Symfony\Component\HttpFoundation\Request; @@ -350,20 +330,17 @@ converter based on Doctrine ObjectManager:: } The service will now be registered in the application, and the -convert method will be used as converter:: +``convert()`` method will be used as converter (using the syntax +``service_name:method_name``):: - $app['converter.user'] = $app->share(function () { + $app['converter.user'] = function () { return new UserConverter(); - }); + }; $app->get('/user/{user}', function (User $user) { // ... })->convert('user', 'converter.user:convert'); -.. warning:: - - Please note that the ability to use a service method (with the `a:b` notation) will be in version 1.2 - Requirements ~~~~~~~~~~~~ @@ -371,8 +348,8 @@ In some cases you may want to only match certain expressions. You can define requirements using regular expressions by calling ``assert`` on the ``Controller`` object, which is returned by the routing methods. -The following will make sure the ``id`` argument is numeric, since ``\d+`` -matches any amount of digits:: +The following will make sure the ``id`` argument is a positive integer, since +``\d+`` matches any amount of digits:: $app->get('/blog/{id}', function ($id) { // ... @@ -387,6 +364,22 @@ You can also chain these calls:: ->assert('postId', '\d+') ->assert('commentId', '\d+'); +Conditions +~~~~~~~~~~ + +Besides restricting route matching based on the HTTP method or parameter +requirements, you can set conditions on any part of the request by calling +``when`` on the ``Controller`` object, which is returned by the routing +methods:: + + $app->get('/blog/{id}', function ($id) { + // ... + }) + ->when("request.headers.get('User-Agent') matches '/firefox/i'"); + +The ``when`` argument is a Symfony Expression_ , which means that you need to +add ``symfony/expression-language`` as a dependency of your project. + Default Values ~~~~~~~~~~~~~~ @@ -404,10 +397,9 @@ have the value ``index``. Named Routes ~~~~~~~~~~~~ -Some providers (such as ``UrlGeneratorProvider``) can make use of named -routes. By default Silex will generate a route name for you, that cannot -really be used. You can give a route a name by calling ``bind`` on the -``Controller`` object that is returned by the routing methods:: +Some providers can make use of named routes. By default Silex will generate an +internal route name for you but you can give an explicit route name by calling +``bind``:: $app->get('/', function () { // ... @@ -419,17 +411,12 @@ really be used. You can give a route a name by calling ``bind`` on the }) ->bind('blog_post'); -.. note:: - - It only makes sense to name routes if you use providers that make use of - the ``RouteCollection``. - -Controllers in Classes +Controllers as Classes ~~~~~~~~~~~~~~~~~~~~~~ -If you don't want to use anonymous functions, you can also define your -controllers as methods. By using the ``ControllerClass::methodName`` syntax, -you can tell Silex to lazily create the controller object for you:: +Instead of anonymous functions, you can also define your controllers as +methods. By using the ``ControllerClass::methodName`` syntax, you can tell +Silex to lazily create the controller object for you:: $app->get('/', 'Acme\\Foo::bar'); @@ -451,14 +438,14 @@ This will load the ``Acme\Foo`` class on demand, create an instance and call the ``bar`` method to get the response. You can use ``Request`` and ``Silex\Application`` type hints to get ``$request`` and ``$app`` injected. -For an even stronger separation between Silex and your controllers, you can -:doc:`define your controllers as services `. +It is also possible to :doc:`define your controllers as services +`. Global Configuration -------------------- -If a controller setting must be applied to all controllers (a converter, a -middleware, a requirement, or a default value), you can configure it on +If a controller setting must be applied to **all** controllers (a converter, a +middleware, a requirement, or a default value), configure it on ``$app['controllers']``, which holds all application controllers:: $app['controllers'] @@ -468,6 +455,7 @@ middleware, a requirement, or a default value), you can configure it on ->method('get') ->convert('id', function () { /* ... */ }) ->before(function () { /* ... */ }) + ->when('request.isSecure() == true') ; These settings are applied to already registered controllers and they become @@ -479,23 +467,20 @@ the defaults for new controllers. mount as they have their own global configuration (read the :doc:`dedicated chapter` for more information). -.. warning:: - - The converters are run for **all** registered controllers. - Error Handlers -------------- -If some part of your code throws an exception you will want to display some -kind of error page to the user. This is what error handlers do. You can also -use them to do additional things, such as logging. +When an exception is thrown, error handlers allow you to display a custom +error page to the user. They can also be used to do additional things, such as +logging. To register an error handler, pass a closure to the ``error`` method which takes an ``Exception`` argument and returns a response:: use Symfony\Component\HttpFoundation\Response; + use Symfony\Component\HttpFoundation\Request; - $app->error(function (\Exception $e, $code) { + $app->error(function (\Exception $e, Request $request, $code) { return new Response('We are sorry, but something went terribly wrong.'); }); @@ -503,8 +488,9 @@ You can also check for specific errors by using the ``$code`` argument, and handle them differently:: use Symfony\Component\HttpFoundation\Response; + use Symfony\Component\HttpFoundation\Request; - $app->error(function (\Exception $e, $code) { + $app->error(function (\Exception $e, Request $request, $code) { switch ($code) { case 404: $message = 'The requested page could not be found.'; @@ -516,26 +502,28 @@ handle them differently:: return new Response($message); }); +You can restrict an error handler to only handle some Exception classes by +setting a more specific type hint for the Closure argument:: + + use Symfony\Component\HttpFoundation\Request; + + $app->error(function (\LogicException $e, Request $request, $code) { + // this handler will only handle \LogicException exceptions + // and exceptions that extend \LogicException + }); + .. note:: As Silex ensures that the Response status code is set to the most appropriate one depending on the exception, setting the status on the - response won't work. If you want to overwrite the status code (which you - should not without a good reason), set the ``X-Status-Code`` header:: + response won't work. If you want to overwrite the status code, set the + ``X-Status-Code`` header:: return new Response('Error', 404 /* ignored */, array('X-Status-Code' => 200)); -You can restrict an error handler to only handle some Exception classes by -setting a more specific type hint for the Closure argument:: - - $app->error(function (\LogicException $e, $code) { - // this handler will only handle \LogicException exceptions - // and exceptions that extends \LogicException - }); - -If you want to set up logging you can use a separate error handler for that. -Just make sure you register it before the response error handlers, because -once a response is returned, the following handlers are ignored. +If you want to use a separate error handler for logging, make sure you register +it with a higher priority than response error handlers, because once a response +is returned, the following handlers are ignored. .. note:: @@ -551,8 +539,9 @@ once a response is returned, the following handlers are ignored. is turned on like this:: use Symfony\Component\HttpFoundation\Response; + use Symfony\Component\HttpFoundation\Request; - $app->error(function (\Exception $e, $code) use ($app) { + $app->error(function (\Exception $e, Request $request, $code) use ($app) { if ($app['debug']) { return; } @@ -571,11 +560,55 @@ early:: return new Response(...); }); +You can convert errors to ``Exceptions``, check out the cookbook :doc:`chapter ` for details. + +View Handlers +------------- + +View Handlers allow you to intercept a controller result that is not a +``Response`` and transform it before it gets returned to the kernel. + +To register a view handler, pass a callable (or string that can be resolved to a +callable) to the ``view()`` method. The callable should accept some sort of result +from the controller:: + + $app->view(function (array $controllerResult) use ($app) { + return $app->json($controllerResult); + }); + +View Handlers also receive the ``Request`` as their second argument, +making them a good candidate for basic content negotiation:: + + $app->view(function (array $controllerResult, Request $request) use ($app) { + $acceptHeader = $request->headers->get('Accept'); + $bestFormat = $app['negotiator']->getBestFormat($acceptHeader, array('json', 'xml')); + + if ('json' === $bestFormat) { + return new JsonResponse($controllerResult); + } + + if ('xml' === $bestFormat) { + return $app['serializer.xml']->renderResponse($controllerResult); + } + + return $controllerResult; + }); + +View Handlers will be examined in the order they are added to the application +and Silex will use type hints to determine if a view handler should be used for +the current result, continuously using the return value of the last view handler +as the input for the next. + +.. note:: + + You must ensure that Silex receives a ``Response`` or a string as the result of + the last view handler (or controller) to be run. + Redirects --------- -You can redirect to another page by returning a redirect response, which you -can create by calling the ``redirect`` method:: +You can redirect to another page by returning a ``RedirectResponse`` response, +which you can create by calling the ``redirect`` method:: $app->get('/', function () use ($app) { return $app->redirect('/hello'); @@ -593,7 +626,7 @@ round-trip to the browser (as for a redirect), use an internal sub-request:: use Symfony\Component\HttpKernel\HttpKernelInterface; $app->get('/', function () use ($app) { - // redirect to /hello + // forward to /hello $subRequest = Request::create('/hello', 'GET'); return $app->handle($subRequest, HttpKernelInterface::SUB_REQUEST); @@ -601,7 +634,7 @@ round-trip to the browser (as for a redirect), use an internal sub-request:: .. tip:: - If you are using ``UrlGeneratorProvider``, you can also generate the URI:: + You can also generate the URI via the built-in URL generator:: $request = Request::create($app['url_generator']->generate('hello'), 'GET'); @@ -632,8 +665,8 @@ response for you:: Streaming --------- -It's possible to create a streaming response, which is important in cases when -you cannot buffer the data being sent:: +It's possible to stream a response, which is important in cases when you don't +want to buffer the data being sent:: $app->get('/images/{file}', function ($file) use ($app) { if (!file_exists(__DIR__.'/images/'.$file)) { @@ -653,9 +686,9 @@ after every chunk:: $stream = function () { $fh = fopen('http://www.example.com/', 'rb'); while (!feof($fh)) { - echo fread($fh, 1024); - ob_flush(); - flush(); + echo fread($fh, 1024); + ob_flush(); + flush(); } fclose($fh); }; @@ -666,7 +699,7 @@ Sending a file If you want to return a file, you can use the ``sendFile`` helper method. It eases returning files that would otherwise not be publicly available. Simply pass it your file path, status code, headers and the content disposition and it -will create a ``BinaryFileResponse`` based response for you:: +will create a ``BinaryFileResponse`` response for you:: $app->get('/files/{path}', function ($path) use ($app) { if (!file_exists('/base/path/' . $path)) { @@ -685,19 +718,11 @@ To further customize the response before returning it, check the API doc for ->setContentDisposition(ResponseHeaderBag::DISPOSITION_ATTACHMENT, 'pic.jpg') ; -.. note:: - - HttpFoundation 2.2 or greater is required for this feature to be available. - Traits ------ Silex comes with PHP traits that define shortcut methods. -.. caution:: - - You need to use PHP 5.4 or later to benefit from this feature. - Almost all built-in service providers have some corresponding PHP traits. To use them, define your own Application class and include the traits you want:: @@ -738,31 +763,37 @@ Make sure to protect your application against attacks. Escaping ~~~~~~~~ -When outputting any user input (either route variables GET/POST variables -obtained from the request), you will have to make sure to escape it correctly, -to prevent Cross-Site-Scripting attacks. +When outputting any user input, make sure to escape it correctly to prevent +Cross-Site-Scripting attacks. * **Escaping HTML**: PHP provides the ``htmlspecialchars`` function for this. Silex provides a shortcut ``escape`` method:: - $app->get('/name', function (Silex\Application $app) { - $name = $app['request']->get('name'); + use Symfony\Component\HttpFoundation\Request; + + $app->get('/name', function (Request $request, Silex\Application $app) { + $name = $request->get('name'); + return "You provided the name {$app->escape($name)}."; }); - If you use the Twig template engine you should use its escaping or even - auto-escaping mechanisms. + If you use the Twig template engine, you should use its escaping or even + auto-escaping mechanisms. Check out the *Providers* :doc:`chapter ` for details. * **Escaping JSON**: If you want to provide data in JSON format you should use the Silex ``json`` function:: - $app->get('/name.json', function (Silex\Application $app) { - $name = $app['request']->get('name'); + use Symfony\Component\HttpFoundation\Request; + + $app->get('/name.json', function (Request $request, Silex\Application $app) { + $name = $request->get('name'); + return $app->json(array('name' => $name)); }); -.. _download: http://silex.sensiolabs.org/download +.. _Silex Skeleton: http://github.com/silexphp/Silex-Skeleton .. _Composer: http://getcomposer.org/ .. _Request: http://api.symfony.com/master/Symfony/Component/HttpFoundation/Request.html .. _Response: http://api.symfony.com/master/Symfony/Component/HttpFoundation/Response.html .. _Monolog: https://github.com/Seldaek/monolog +.. _Expression: https://symfony.com/doc/current/book/routing.html#completely-customized-route-matching-with-conditions diff --git a/lib/silex/vendor/silex/silex/doc/web_servers.rst b/lib/silex/vendor/silex/silex/doc/web_servers.rst index 6c66ecc7c..18f5531d4 100644 --- a/lib/silex/vendor/silex/silex/doc/web_servers.rst +++ b/lib/silex/vendor/silex/silex/doc/web_servers.rst @@ -14,6 +14,7 @@ following ``.htaccess`` file: RewriteEngine On #RewriteBase /path/to/app + RewriteCond %{REQUEST_FILENAME} !-d RewriteCond %{REQUEST_FILENAME} !-f RewriteRule ^ index.php [QSA,L] @@ -25,52 +26,78 @@ following ``.htaccess`` file: relative from the webroot. Alternatively, if you use Apache 2.2.16 or higher, you can use the -`FallbackResource directive`_ so make your .htaccess even easier: +`FallbackResource directive`_ to make your .htaccess even easier: .. code-block:: apache - FallbackResource /index.php + FallbackResource index.php .. note:: If your site is not at the webroot level you will have to adjust the path to point to your directory, relative from the webroot. + +Or if you're using a VirtualHost, you can add the same directive to the VirtualHost's Directory entry: + +.. code-block:: apache + + + # other directives + + Alias /app/ /path/to/app/ + + # other directives + + FallbackResource /app/index.php + + + +.. note:: + + Note that you need the leading forward slash there, unlike with the .htaccess version nginx ----- -If you are using nginx, configure your vhost to forward non-existent -resources to ``index.php``: +The **minimum configuration** to get your application running under Nginx is: .. code-block:: nginx server { - #site root is redirected to the app boot script - location = / { - try_files @site @site; - } - - #all other locations try other files first and go to our front controller if none of them exists + server_name domain.tld www.domain.tld; + root /var/www/project/web; + location / { - try_files $uri $uri/ @site; + # try to serve file directly, fallback to front controller + try_files $uri /index.php$is_args$args; + } + + # If you have 2 front controllers for dev|prod use the following line instead + # location ~ ^/(index|index_dev)\.php(/|$) { + location ~ ^/index\.php(/|$) { + # the ubuntu default + fastcgi_pass unix:/var/run/php/phpX.X-fpm.sock; + # for running on centos + #fastcgi_pass unix:/var/run/php-fpm/www.sock; + + fastcgi_split_path_info ^(.+\.php)(/.*)$; + include fastcgi_params; + fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; + fastcgi_param HTTPS off; + + # Prevents URIs that include the front controller. This will 404: + # http://domain.tld/index.php/some-path + # Enable the internal directive to disable URIs like this + # internal; } #return 404 for all php files as we do have a front controller location ~ \.php$ { return 404; } - - location @site { - # the ubuntu default - fastcgi_pass unix:/var/run/php5-fpm.sock; - # for running on centos - #fastcgi_pass unix:/var/run/php-fpm/www.sock; - - include fastcgi_params; - fastcgi_param SCRIPT_FILENAME $document_root/index.php; - #uncomment when running via https - #fastcgi_param HTTPS on; - } + + error_log /var/log/nginx/project_error.log; + access_log /var/log/nginx/project_access.log; } IIS @@ -124,13 +151,12 @@ point: .. _FallbackResource directive: http://www.adayinthelifeof.nl/2012/01/21/apaches-fallbackresource-your-new-htaccess-command/ -PHP 5.4 -------- +PHP +--- -PHP 5.4 ships with a built-in webserver for development. This server allows -you to run silex without any configuration. However, in order to serve static -files, you'll have to make sure your front controller returns false in that -case:: +PHP ships with a built-in webserver for development. This server allows you to +run silex without any configuration. However, in order to serve static files, +you'll have to make sure your front controller returns false in that case:: // web/index.php diff --git a/lib/silex/vendor/silex/silex/phpunit.xml.dist b/lib/silex/vendor/silex/silex/phpunit.xml.dist index 782f0cec1..799f16c90 100644 --- a/lib/silex/vendor/silex/silex/phpunit.xml.dist +++ b/lib/silex/vendor/silex/silex/phpunit.xml.dist @@ -9,7 +9,7 @@ processIsolation="false" stopOnFailure="false" syntaxCheck="false" - bootstrap="tests/bootstrap.php" + bootstrap="vendor/autoload.php" > diff --git a/lib/silex/vendor/silex/silex/src/Silex/Api/BootableProviderInterface.php b/lib/silex/vendor/silex/silex/src/Silex/Api/BootableProviderInterface.php new file mode 100644 index 000000000..739e04d56 --- /dev/null +++ b/lib/silex/vendor/silex/silex/src/Silex/Api/BootableProviderInterface.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 Silex\Api; + +use Silex\Application; + +/** + * Interface for bootable service providers. + * + * @author Fabien Potencier + */ +interface BootableProviderInterface +{ + /** + * Bootstraps the application. + * + * This method is called after all services are registered + * and should be used for "dynamic" configuration (whenever + * a service must be requested). + * + * @param Application $app + */ + public function boot(Application $app); +} diff --git a/lib/silex/vendor/silex/silex/src/Silex/Api/ControllerProviderInterface.php b/lib/silex/vendor/silex/silex/src/Silex/Api/ControllerProviderInterface.php new file mode 100644 index 000000000..28d9d0e5e --- /dev/null +++ b/lib/silex/vendor/silex/silex/src/Silex/Api/ControllerProviderInterface.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 Silex\Api; + +use Silex\Application; +use Silex\ControllerCollection; + +/** + * Interface for controller providers. + * + * @author Fabien Potencier + */ +interface ControllerProviderInterface +{ + /** + * Returns routes to connect to the given application. + * + * @param Application $app An Application instance + * + * @return ControllerCollection A ControllerCollection instance + */ + public function connect(Application $app); +} diff --git a/lib/silex/vendor/silex/silex/src/Silex/Api/EventListenerProviderInterface.php b/lib/silex/vendor/silex/silex/src/Silex/Api/EventListenerProviderInterface.php new file mode 100644 index 000000000..f3e625557 --- /dev/null +++ b/lib/silex/vendor/silex/silex/src/Silex/Api/EventListenerProviderInterface.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 Silex\Api; + +use Symfony\Component\EventDispatcher\EventDispatcherInterface; +use Pimple\Container; + +/** + * Interface for event listener providers. + * + * @author Fabien Potencier + */ +interface EventListenerProviderInterface +{ + public function subscribe(Container $app, EventDispatcherInterface $dispatcher); +} diff --git a/lib/silex/vendor/silex/silex/src/Silex/Api/LICENSE b/lib/silex/vendor/silex/silex/src/Silex/Api/LICENSE new file mode 100644 index 000000000..bc6ad0497 --- /dev/null +++ b/lib/silex/vendor/silex/silex/src/Silex/Api/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2010-2015 Fabien Potencier + +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/lib/silex/vendor/silex/silex/src/Silex/Api/composer.json b/lib/silex/vendor/silex/silex/src/Silex/Api/composer.json new file mode 100644 index 000000000..7290988a3 --- /dev/null +++ b/lib/silex/vendor/silex/silex/src/Silex/Api/composer.json @@ -0,0 +1,34 @@ +{ + "minimum-stability": "dev", + "name": "silex/api", + "description": "The Silex interfaces", + "keywords": ["microframework"], + "homepage": "http://silex.sensiolabs.org", + "license": "MIT", + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Igor Wiedler", + "email": "igor@wiedler.ch" + } + ], + "require": { + "php": ">=5.5.9", + "pimple/pimple": "~3.0" + }, + "suggest": { + "symfony/event-dispatcher": "For EventListenerProviderInterface", + "silex/silex": "For BootableProviderInterface and ControllerProviderInterface" + }, + "autoload": { + "psr-4": { "Silex\\Api\\": "" } + }, + "extra": { + "branch-alias": { + "dev-master": "2.2.x-dev" + } + } +} diff --git a/lib/silex/vendor/silex/silex/src/Silex/AppArgumentValueResolver.php b/lib/silex/vendor/silex/silex/src/Silex/AppArgumentValueResolver.php new file mode 100644 index 000000000..cc2197ab8 --- /dev/null +++ b/lib/silex/vendor/silex/silex/src/Silex/AppArgumentValueResolver.php @@ -0,0 +1,47 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Silex; + +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpKernel\Controller\ArgumentValueResolverInterface; +use Symfony\Component\HttpKernel\ControllerMetadata\ArgumentMetadata; + +/** + * HttpKernel Argument Resolver for Silex. + * + * @author Romain Neutron + */ +class AppArgumentValueResolver implements ArgumentValueResolverInterface +{ + private $app; + + public function __construct(Application $app) + { + $this->app = $app; + } + + /** + * {@inheritdoc} + */ + public function supports(Request $request, ArgumentMetadata $argument) + { + return null !== $argument->getType() && ($argument->getType() === Application::class || is_subclass_of($argument->getType(), Application::class)); + } + + /** + * {@inheritdoc} + */ + public function resolve(Request $request, ArgumentMetadata $argument) + { + yield $this->app; + } +} diff --git a/lib/silex/vendor/silex/silex/src/Silex/Application.php b/lib/silex/vendor/silex/silex/src/Silex/Application.php index d14b541b6..a5e5431d8 100644 --- a/lib/silex/vendor/silex/silex/src/Silex/Application.php +++ b/lib/silex/vendor/silex/silex/src/Silex/Application.php @@ -11,41 +11,40 @@ namespace Silex; +use Pimple\Container; +use Pimple\ServiceProviderInterface; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\HttpFoundation\BinaryFileResponse; -use Symfony\Component\HttpKernel\HttpKernel; use Symfony\Component\HttpKernel\HttpKernelInterface; use Symfony\Component\HttpKernel\TerminableInterface; use Symfony\Component\HttpKernel\Event\FilterResponseEvent; use Symfony\Component\HttpKernel\Event\GetResponseEvent; use Symfony\Component\HttpKernel\Event\PostResponseEvent; -use Symfony\Component\HttpKernel\EventListener\ResponseListener; -use Symfony\Component\HttpKernel\EventListener\RouterListener; use Symfony\Component\HttpKernel\Exception\HttpException; use Symfony\Component\HttpKernel\KernelEvents; use Symfony\Component\HttpFoundation\Request; -use Symfony\Component\HttpFoundation\RequestStack; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\HttpFoundation\StreamedResponse; use Symfony\Component\HttpFoundation\JsonResponse; -use Symfony\Component\Routing\RouteCollection; -use Symfony\Component\Routing\RequestContext; -use Silex\EventListener\LocaleListener; -use Silex\EventListener\MiddlewareListener; -use Silex\EventListener\ConverterListener; -use Silex\EventListener\StringToResponseListener; +use Silex\Api\BootableProviderInterface; +use Silex\Api\EventListenerProviderInterface; +use Silex\Api\ControllerProviderInterface; +use Silex\Provider\ExceptionHandlerServiceProvider; +use Silex\Provider\RoutingServiceProvider; +use Silex\Provider\HttpKernelServiceProvider; /** * The Silex framework class. * * @author Fabien Potencier */ -class Application extends \Pimple implements HttpKernelInterface, TerminableInterface +class Application extends Container implements HttpKernelInterface, TerminableInterface { - const VERSION = '1.2.5'; + const VERSION = '2.2.2'; const EARLY_EVENT = 512; - const LATE_EVENT = -512; + const LATE_EVENT = -512; protected $providers = array(); protected $booted = false; @@ -61,93 +60,15 @@ class Application extends \Pimple implements HttpKernelInterface, TerminableInte { parent::__construct(); - $app = $this; - - $this['logger'] = null; - - $this['routes'] = $this->share(function () { - return new RouteCollection(); - }); - - $this['controllers'] = $this->share(function () use ($app) { - return $app['controllers_factory']; - }); - - $this['controllers_factory'] = function () use ($app) { - return new ControllerCollection($app['route_factory']); - }; - - $this['route_class'] = 'Silex\\Route'; - $this['route_factory'] = function () use ($app) { - return new $app['route_class'](); - }; - - $this['exception_handler'] = $this->share(function () use ($app) { - return new ExceptionHandler($app['debug']); - }); - - $this['dispatcher_class'] = 'Symfony\\Component\\EventDispatcher\\EventDispatcher'; - $this['dispatcher'] = $this->share(function () use ($app) { - $dispatcher = new $app['dispatcher_class'](); - - $urlMatcher = new LazyUrlMatcher(function () use ($app) { - return $app['url_matcher']; - }); - $dispatcher->addSubscriber(new RouterListener($urlMatcher, $app['request_context'], $app['logger'], $app['request_stack'])); - $dispatcher->addSubscriber(new LocaleListener($app, $urlMatcher, $app['request_stack'])); - if (isset($app['exception_handler'])) { - $dispatcher->addSubscriber($app['exception_handler']); - } - $dispatcher->addSubscriber(new ResponseListener($app['charset'])); - $dispatcher->addSubscriber(new MiddlewareListener($app)); - $dispatcher->addSubscriber(new ConverterListener($app['routes'], $app['callback_resolver'])); - $dispatcher->addSubscriber(new StringToResponseListener()); - - return $dispatcher; - }); - - $this['callback_resolver'] = $this->share(function () use ($app) { - return new CallbackResolver($app); - }); - - $this['resolver'] = $this->share(function () use ($app) { - return new ControllerResolver($app, $app['logger']); - }); - - $this['kernel'] = $this->share(function () use ($app) { - return new HttpKernel($app['dispatcher'], $app['resolver'], $app['request_stack']); - }); - - $this['request_stack'] = $this->share(function () use ($app) { - if (class_exists('Symfony\Component\HttpFoundation\RequestStack')) { - return new RequestStack(); - } - }); - - $this['request_context'] = $this->share(function () use ($app) { - $context = new RequestContext(); - - $context->setHttpPort($app['request.http_port']); - $context->setHttpsPort($app['request.https_port']); - - return $context; - }); - - $this['url_matcher'] = $this->share(function () use ($app) { - return new RedirectableUrlMatcher($app['routes'], $app['request_context']); - }); - - $this['request_error'] = $this->protect(function () { - throw new \RuntimeException('Accessed request service outside of request scope. Try moving that call to a before handler or controller.'); - }); - - $this['request'] = $this['request_error']; - $this['request.http_port'] = 80; $this['request.https_port'] = 443; $this['debug'] = false; $this['charset'] = 'UTF-8'; - $this['locale'] = 'en'; + $this['logger'] = null; + + $this->register(new HttpKernelServiceProvider()); + $this->register(new RoutingServiceProvider()); + $this->register(new ExceptionHandlerServiceProvider()); foreach ($values as $key => $value) { $this[$key] = $value; @@ -166,11 +87,7 @@ class Application extends \Pimple implements HttpKernelInterface, TerminableInte { $this->providers[] = $provider; - $provider->register($this); - - foreach ($values as $key => $value) { - $this[$key] = $value; - } + parent::register($provider, $values); return $this; } @@ -183,12 +100,20 @@ class Application extends \Pimple implements HttpKernelInterface, TerminableInte */ public function boot() { - if (!$this->booted) { - foreach ($this->providers as $provider) { - $provider->boot($this); + if ($this->booted) { + return; + } + + $this->booted = true; + + foreach ($this->providers as $provider) { + if ($provider instanceof EventListenerProviderInterface) { + $provider->subscribe($this, $this['dispatcher']); } - $this->booted = true; + if ($provider instanceof BootableProviderInterface) { + $provider->boot($this); + } } } @@ -259,6 +184,19 @@ class Application extends \Pimple implements HttpKernelInterface, TerminableInte return $this['controllers']->delete($pattern, $to); } + /** + * Maps an OPTIONS request to a callable. + * + * @param string $pattern Matched route pattern + * @param mixed $to Callback that returns the response when matched + * + * @return Controller + */ + public function options($pattern, $to = null) + { + return $this['controllers']->options($pattern, $to); + } + /** * Maps a PATCH request to a callable. * @@ -288,11 +226,11 @@ class Application extends \Pimple implements HttpKernelInterface, TerminableInte return; } - $this['dispatcher'] = $this->share($this->extend('dispatcher', function ($dispatcher, $app) use ($callback, $priority, $eventName) { + $this->extend('dispatcher', function (EventDispatcherInterface $dispatcher, $app) use ($callback, $priority, $eventName) { $dispatcher->addListener($eventName, $app['callback_resolver']->resolveCallback($callback), $priority); return $dispatcher; - })); + }); } /** @@ -309,7 +247,7 @@ class Application extends \Pimple implements HttpKernelInterface, TerminableInte $app = $this; $this->on(KernelEvents::REQUEST, function (GetResponseEvent $event) use ($callback, $app) { - if (HttpKernelInterface::MASTER_REQUEST !== $event->getRequestType()) { + if (!$event->isMasterRequest()) { return; } @@ -335,7 +273,7 @@ class Application extends \Pimple implements HttpKernelInterface, TerminableInte $app = $this; $this->on(KernelEvents::RESPONSE, function (FilterResponseEvent $event) use ($callback, $app) { - if (HttpKernelInterface::MASTER_REQUEST !== $event->getRequestType()) { + if (!$event->isMasterRequest()) { return; } @@ -401,13 +339,28 @@ class Application extends \Pimple implements HttpKernelInterface, TerminableInte } /** - * Flushes the controller collection. + * Registers a view handler. * - * @param string $prefix The route prefix + * View handlers are simple callables which take a controller result and the + * request as arguments, whenever a controller returns a value that is not + * an instance of Response. When this occurs, all suitable handlers will be + * called, until one returns a Response object. + * + * @param mixed $callback View handler callback + * @param int $priority The higher this value, the earlier an event + * listener will be triggered in the chain (defaults to 0) */ - public function flush($prefix = '') + public function view($callback, $priority = 0) { - $this['routes']->addCollection($this['controllers']->flush($prefix)); + $this->on(KernelEvents::VIEW, new ViewListenerWrapper($this, $callback), $priority); + } + + /** + * Flushes the controller collection. + */ + public function flush() + { + $this['routes']->addCollection($this['controllers']->flush()); } /** @@ -475,8 +428,6 @@ class Application extends \Pimple implements HttpKernelInterface, TerminableInte * @param null|string $contentDisposition The type of Content-Disposition to set automatically with the filename * * @return BinaryFileResponse - * - * @throws \RuntimeException When the feature is not supported, before http-foundation v2.2 */ public function sendFile($file, $status = 200, array $headers = array(), $contentDisposition = null) { @@ -486,10 +437,12 @@ class Application extends \Pimple implements HttpKernelInterface, TerminableInte /** * Mounts controllers under the given route prefix. * - * @param string $prefix The route prefix - * @param ControllerCollection|ControllerProviderInterface $controllers A ControllerCollection or a ControllerProviderInterface instance + * @param string $prefix The route prefix + * @param ControllerCollection|callable|ControllerProviderInterface $controllers A ControllerCollection, a callable, or a ControllerProviderInterface instance * * @return Application + * + * @throws \LogicException */ public function mount($prefix, $controllers) { @@ -501,8 +454,8 @@ class Application extends \Pimple implements HttpKernelInterface, TerminableInte } $controllers = $connectedControllers; - } elseif (!$controllers instanceof ControllerCollection) { - throw new \LogicException('The "mount" method takes either a "ControllerCollection" or a "ControllerProviderInterface" instance.'); + } elseif (!$controllers instanceof ControllerCollection && !is_callable($controllers)) { + throw new \LogicException('The "mount" method takes either a "ControllerCollection" instance, "ControllerProviderInterface" instance, or a callable.'); } $this['controllers']->mount($prefix, $controllers); @@ -538,17 +491,9 @@ class Application extends \Pimple implements HttpKernelInterface, TerminableInte $this->boot(); } - $current = HttpKernelInterface::SUB_REQUEST === $type ? $this['request'] : $this['request_error']; - - $this['request'] = $request; - $this->flush(); - $response = $this['kernel']->handle($request, $type, $catch); - - $this['request'] = $current; - - return $response; + return $this['kernel']->handle($request, $type, $catch); } /** diff --git a/lib/silex/vendor/silex/silex/src/Silex/Application/FormTrait.php b/lib/silex/vendor/silex/silex/src/Silex/Application/FormTrait.php index 53717c60d..9106e7de1 100644 --- a/lib/silex/vendor/silex/silex/src/Silex/Application/FormTrait.php +++ b/lib/silex/vendor/silex/silex/src/Silex/Application/FormTrait.php @@ -11,25 +11,44 @@ namespace Silex\Application; +use Symfony\Component\Form\Extension\Core\Type\FormType; use Symfony\Component\Form\FormBuilder; +use Symfony\Component\Form\FormTypeInterface; /** * Form trait. * * @author Fabien Potencier + * @author David Berlioz */ trait FormTrait { /** - * Creates and returns a form builder instance + * Creates and returns a form builder instance. * - * @param mixed $data The initial data for the form - * @param array $options Options for the form + * @param mixed $data The initial data for the form + * @param array $options Options for the form + * @param string|FormTypeInterface $type Type of the form * * @return FormBuilder */ - public function form($data = null, array $options = array()) + public function form($data = null, array $options = array(), $type = null) { - return $this['form.factory']->createBuilder('form', $data, $options); + return $this['form.factory']->createBuilder($type ?: FormType::class, $data, $options); + } + + /** + * Creates and returns a named form builder instance. + * + * @param string $name + * @param mixed $data The initial data for the form + * @param array $options Options for the form + * @param string|FormTypeInterface $type Type of the form + * + * @return FormBuilder + */ + public function namedForm($name, $data = null, array $options = array(), $type = null) + { + return $this['form.factory']->createNamedBuilder($name, $type ?: FormType::class, $data, $options); } } diff --git a/lib/silex/vendor/silex/silex/src/Silex/Application/SecurityTrait.php b/lib/silex/vendor/silex/silex/src/Silex/Application/SecurityTrait.php index 0bfaee3a1..43ce55522 100644 --- a/lib/silex/vendor/silex/silex/src/Silex/Application/SecurityTrait.php +++ b/lib/silex/vendor/silex/silex/src/Silex/Application/SecurityTrait.php @@ -11,7 +11,7 @@ namespace Silex\Application; -use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; +use Symfony\Component\Security\Core\Exception\AuthenticationCredentialsNotFoundException; use Symfony\Component\Security\Core\User\UserInterface; /** @@ -21,26 +21,6 @@ use Symfony\Component\Security\Core\User\UserInterface; */ trait SecurityTrait { - /** - * Gets a user from the Security Context. - * - * @return mixed - * - * @see TokenInterface::getUser() - */ - public function user() - { - if (null === $token = $this['security']->getToken()) { - return; - } - - if (!is_object($user = $token->getUser())) { - return; - } - - return $user; - } - /** * Encodes the raw password. * @@ -55,4 +35,19 @@ trait SecurityTrait { return $this['security.encoder_factory']->getEncoder($user)->encodePassword($password, $user->getSalt()); } + + /** + * Checks if the attributes are granted against the current authentication token and optionally supplied object. + * + * @param mixed $attributes + * @param mixed $object + * + * @return bool + * + * @throws AuthenticationCredentialsNotFoundException when the token storage has no authentication token. + */ + public function isGranted($attributes, $object = null) + { + return $this['security.authorization_checker']->isGranted($attributes, $object); + } } diff --git a/lib/silex/vendor/silex/silex/src/Silex/CallbackResolver.php b/lib/silex/vendor/silex/silex/src/Silex/CallbackResolver.php index 859d86e24..692901c29 100644 --- a/lib/silex/vendor/silex/silex/src/Silex/CallbackResolver.php +++ b/lib/silex/vendor/silex/silex/src/Silex/CallbackResolver.php @@ -11,13 +11,15 @@ namespace Silex; +use Pimple\Container; + class CallbackResolver { const SERVICE_PATTERN = "/[A-Za-z0-9\._\-]+:[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*/"; private $app; - public function __construct(\Pimple $app) + public function __construct(Container $app) { $this->app = $app; } @@ -31,7 +33,7 @@ class CallbackResolver */ public function isValid($name) { - return is_string($name) && preg_match(static::SERVICE_PATTERN, $name); + return is_string($name) && (preg_match(static::SERVICE_PATTERN, $name) || isset($this->app[$name])); } /** @@ -39,19 +41,25 @@ class CallbackResolver * * @param string $name * - * @return array A callable array + * @return callable * * @throws \InvalidArgumentException In case the method does not exist. */ public function convertCallback($name) { - list($service, $method) = explode(':', $name, 2); - - if (!isset($this->app[$service])) { - throw new \InvalidArgumentException(sprintf('Service "%s" does not exist.', $service)); + if (preg_match(static::SERVICE_PATTERN, $name)) { + list($service, $method) = explode(':', $name, 2); + $callback = array($this->app[$service], $method); + } else { + $service = $name; + $callback = $this->app[$name]; } - return array($this->app[$service], $method); + if (!is_callable($callback)) { + throw new \InvalidArgumentException(sprintf('Service "%s" is not callable.', $service)); + } + + return $callback; } /** @@ -59,7 +67,7 @@ class CallbackResolver * * @param string $name * - * @return array A callable array + * @return string|callable A callable value or the string passed in * * @throws \InvalidArgumentException In case the method does not exist. */ diff --git a/lib/silex/vendor/silex/silex/src/Silex/Controller.php b/lib/silex/vendor/silex/silex/src/Silex/Controller.php index 99bf3a588..9a8075591 100644 --- a/lib/silex/vendor/silex/silex/src/Silex/Controller.php +++ b/lib/silex/vendor/silex/silex/src/Silex/Controller.php @@ -27,6 +27,8 @@ use Silex\Exception\ControllerFrozenException; * @method Controller requireHttps() * @method Controller before(mixed $callback) * @method Controller after(mixed $callback) + * @method Controller when(string $condition) + * * @author Igor Wiedler */ class Controller @@ -106,13 +108,15 @@ class Controller public function generateRouteName($prefix) { - $requirements = $this->route->getRequirements(); - $method = isset($requirements['_method']) ? $requirements['_method'] : ''; + $methods = implode('_', $this->route->getMethods()).'_'; - $routeName = $prefix.$method.$this->route->getPath(); + $routeName = $methods.$prefix.$this->route->getPath(); $routeName = str_replace(array('/', ':', '|', '-'), '_', $routeName); $routeName = preg_replace('/[^a-z0-9A-Z_.]+/', '', $routeName); + // Collapse consecutive underscores down into a single underscore. + $routeName = preg_replace('/_+/', '_', $routeName); + return $routeName; } } diff --git a/lib/silex/vendor/silex/silex/src/Silex/ControllerCollection.php b/lib/silex/vendor/silex/silex/src/Silex/ControllerCollection.php index e66f87e68..32f4a1917 100644 --- a/lib/silex/vendor/silex/silex/src/Silex/ControllerCollection.php +++ b/lib/silex/vendor/silex/silex/src/Silex/ControllerCollection.php @@ -32,6 +32,7 @@ use Symfony\Component\HttpFoundation\Request; * @method ControllerCollection requireHttps() * @method ControllerCollection before(mixed $callback) * @method ControllerCollection after(mixed $callback) + * @method ControllerCollection when(string $condition) * * @author Igor Wiedler * @author Fabien Potencier @@ -42,10 +43,14 @@ class ControllerCollection protected $defaultRoute; protected $defaultController; protected $prefix; + protected $routesFactory; + protected $controllersFactory; - public function __construct(Route $defaultRoute) + public function __construct(Route $defaultRoute, RouteCollection $routesFactory = null, $controllersFactory = null) { $this->defaultRoute = $defaultRoute; + $this->routesFactory = $routesFactory; + $this->controllersFactory = $controllersFactory; $this->defaultController = function (Request $request) { throw new \LogicException(sprintf('The "%s" route must have code to run when it matches.', $request->attributes->get('_route'))); }; @@ -54,11 +59,22 @@ class ControllerCollection /** * Mounts controllers under the given route prefix. * - * @param string $prefix The route prefix - * @param ControllerCollection $controllers A ControllerCollection instance + * @param string $prefix The route prefix + * @param ControllerCollection|callable $controllers A ControllerCollection instance or a callable for defining routes + * + * @throws \LogicException */ - public function mount($prefix, ControllerCollection $controllers) + public function mount($prefix, $controllers) { + if (is_callable($controllers)) { + $collection = $this->controllersFactory ? call_user_func($this->controllersFactory) : new static(new Route(), new RouteCollection()); + $collection->defaultRoute = clone $this->defaultRoute; + call_user_func($controllers, $collection); + $controllers = $collection; + } elseif (!$controllers instanceof self) { + throw new \LogicException('The "mount" method takes either a "ControllerCollection" instance or callable.'); + } + $controllers->prefix = $prefix; $this->controllers[] = $controllers; @@ -136,6 +152,19 @@ class ControllerCollection return $this->match($pattern, $to)->method('DELETE'); } + /** + * Maps an OPTIONS request to a callable. + * + * @param string $pattern Matched route pattern + * @param mixed $to Callback that returns the response when matched + * + * @return Controller + */ + public function options($pattern, $to = null) + { + return $this->match($pattern, $to)->method('OPTIONS'); + } + /** * Maps a PATCH request to a callable. * @@ -158,9 +187,7 @@ class ControllerCollection call_user_func_array(array($this->defaultRoute, $method), $arguments); foreach ($this->controllers as $controller) { - if ($controller instanceof Controller) { - call_user_func_array(array($controller, $method), $arguments); - } + call_user_func_array(array($controller, $method), $arguments); } return $this; @@ -169,32 +196,43 @@ class ControllerCollection /** * Persists and freezes staged controllers. * - * @param string $prefix - * * @return RouteCollection A RouteCollection instance */ - public function flush($prefix = '') + public function flush() { - $routes = new RouteCollection(); + if (null === $this->routesFactory) { + $routes = new RouteCollection(); + } else { + $routes = $this->routesFactory; + } + + return $this->doFlush('', $routes); + } + + private function doFlush($prefix, RouteCollection $routes) + { + if ($prefix !== '') { + $prefix = '/'.trim(trim($prefix), '/'); + } foreach ($this->controllers as $controller) { if ($controller instanceof Controller) { + $controller->getRoute()->setPath($prefix.$controller->getRoute()->getPath()); if (!$name = $controller->getRouteName()) { - $name = $controller->generateRouteName($prefix); + $name = $base = $controller->generateRouteName(''); + $i = 0; while ($routes->get($name)) { - $name .= '_'; + $name = $base.'_'.++$i; } $controller->bind($name); } $routes->add($name, $controller->getRoute()); $controller->freeze(); } else { - $routes->addCollection($controller->flush($controller->prefix)); + $controller->doFlush($prefix.$controller->prefix, $routes); } } - $routes->addPrefix($prefix); - $this->controllers = array(); return $routes; diff --git a/lib/silex/vendor/silex/silex/src/Silex/ControllerResolver.php b/lib/silex/vendor/silex/silex/src/Silex/ControllerResolver.php index 5955f8f81..0a95e15f2 100644 --- a/lib/silex/vendor/silex/silex/src/Silex/ControllerResolver.php +++ b/lib/silex/vendor/silex/silex/src/Silex/ControllerResolver.php @@ -19,6 +19,8 @@ use Symfony\Component\HttpFoundation\Request; * Adds Application as a valid argument for controllers. * * @author Fabien Potencier + * + * @deprecated This class can be dropped once Symfony 3.0 is not supported anymore. */ class ControllerResolver extends BaseControllerResolver { diff --git a/lib/silex/vendor/silex/silex/src/Silex/EventListener/LogListener.php b/lib/silex/vendor/silex/silex/src/Silex/EventListener/LogListener.php index 9211d072d..5f3cc9045 100644 --- a/lib/silex/vendor/silex/silex/src/Silex/EventListener/LogListener.php +++ b/lib/silex/vendor/silex/silex/src/Silex/EventListener/LogListener.php @@ -12,8 +12,8 @@ namespace Silex\EventListener; use Psr\Log\LoggerInterface; +use Psr\Log\LogLevel; use Symfony\Component\EventDispatcher\EventSubscriberInterface; -use Symfony\Component\HttpKernel\HttpKernelInterface; use Symfony\Component\HttpKernel\Event\GetResponseEvent; use Symfony\Component\HttpKernel\Event\FilterResponseEvent; use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent; @@ -24,15 +24,27 @@ use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\RedirectResponse; /** - * Log request, response and exceptions. + * Logs request, response, and exceptions. */ class LogListener implements EventSubscriberInterface { protected $logger; + protected $exceptionLogFilter; - public function __construct(LoggerInterface $logger) + public function __construct(LoggerInterface $logger, $exceptionLogFilter = null) { $this->logger = $logger; + if (null === $exceptionLogFilter) { + $exceptionLogFilter = function (\Exception $e) { + if ($e instanceof HttpExceptionInterface && $e->getStatusCode() < 500) { + return LogLevel::ERROR; + } + + return LogLevel::CRITICAL; + }; + } + + $this->exceptionLogFilter = $exceptionLogFilter; } /** @@ -42,7 +54,7 @@ class LogListener implements EventSubscriberInterface */ public function onKernelRequest(GetResponseEvent $event) { - if (HttpKernelInterface::MASTER_REQUEST !== $event->getRequestType()) { + if (!$event->isMasterRequest()) { return; } @@ -56,7 +68,7 @@ class LogListener implements EventSubscriberInterface */ public function onKernelResponse(FilterResponseEvent $event) { - if (HttpKernelInterface::MASTER_REQUEST !== $event->getRequestType()) { + if (!$event->isMasterRequest()) { return; } @@ -80,7 +92,7 @@ class LogListener implements EventSubscriberInterface */ protected function logRequest(Request $request) { - $this->logger->info('> '.$request->getMethod().' '.$request->getRequestUri()); + $this->logger->log(LogLevel::DEBUG, '> '.$request->getMethod().' '.$request->getRequestUri()); } /** @@ -90,27 +102,21 @@ class LogListener implements EventSubscriberInterface */ protected function logResponse(Response $response) { + $message = '< '.$response->getStatusCode(); + if ($response instanceof RedirectResponse) { - $this->logger->info('< '.$response->getStatusCode().' '.$response->getTargetUrl()); - } else { - $this->logger->info('< '.$response->getStatusCode()); + $message .= ' '.$response->getTargetUrl(); } + + $this->logger->log(LogLevel::DEBUG, $message); } /** * Logs an exception. - * - * @param \Exception $e */ protected function logException(\Exception $e) { - $message = sprintf('%s: %s (uncaught exception) at %s line %s', get_class($e), $e->getMessage(), $e->getFile(), $e->getLine()); - - if ($e instanceof HttpExceptionInterface && $e->getStatusCode() < 500) { - $this->logger->error($message, array('exception' => $e)); - } else { - $this->logger->critical($message, array('exception' => $e)); - } + $this->logger->log(call_user_func($this->exceptionLogFilter, $e), sprintf('%s: %s (uncaught exception) at %s line %s', get_class($e), $e->getMessage(), $e->getFile(), $e->getLine()), array('exception' => $e)); } public static function getSubscribedEvents() diff --git a/lib/silex/vendor/silex/silex/src/Silex/EventListener/MiddlewareListener.php b/lib/silex/vendor/silex/silex/src/Silex/EventListener/MiddlewareListener.php index 0fecb81ef..9b28ff1ac 100644 --- a/lib/silex/vendor/silex/silex/src/Silex/EventListener/MiddlewareListener.php +++ b/lib/silex/vendor/silex/silex/src/Silex/EventListener/MiddlewareListener.php @@ -89,7 +89,7 @@ class MiddlewareListener implements EventSubscriberInterface { return array( // this must be executed after the late events defined with before() (and their priority is -512) - KernelEvents::REQUEST => array('onKernelRequest', -1024), + KernelEvents::REQUEST => array('onKernelRequest', -1024), KernelEvents::RESPONSE => array('onKernelResponse', 128), ); } diff --git a/lib/silex/vendor/silex/silex/src/Silex/Exception/ControllerFrozenException.php b/lib/silex/vendor/silex/silex/src/Silex/Exception/ControllerFrozenException.php index 543fd9189..7f0d65f14 100644 --- a/lib/silex/vendor/silex/silex/src/Silex/Exception/ControllerFrozenException.php +++ b/lib/silex/vendor/silex/silex/src/Silex/Exception/ControllerFrozenException.php @@ -12,7 +12,7 @@ namespace Silex\Exception; /** - * Exception, is thrown when a frozen controller is modified + * Exception, is thrown when a frozen controller is modified. * * @author Igor Wiedler */ diff --git a/lib/silex/vendor/silex/silex/src/Silex/ExceptionHandler.php b/lib/silex/vendor/silex/silex/src/Silex/ExceptionHandler.php index 27850c990..34eb8937c 100644 --- a/lib/silex/vendor/silex/silex/src/Silex/ExceptionHandler.php +++ b/lib/silex/vendor/silex/silex/src/Silex/ExceptionHandler.php @@ -12,40 +12,38 @@ namespace Silex; use Symfony\Component\Debug\ExceptionHandler as DebugExceptionHandler; +use Symfony\Component\Debug\Exception\FlattenException; use Symfony\Component\EventDispatcher\EventSubscriberInterface; +use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent; use Symfony\Component\HttpKernel\KernelEvents; /** - * Defaults exception handler. + * Default exception handler. * * @author Fabien Potencier */ class ExceptionHandler implements EventSubscriberInterface { protected $debug; - protected $enabled; public function __construct($debug) { $this->debug = $debug; - $this->enabled = true; - } - - public function disable() - { - $this->enabled = false; } public function onSilexError(GetResponseForExceptionEvent $event) { - if (!$this->enabled) { - return; - } - $handler = new DebugExceptionHandler($this->debug); - $event->setResponse($handler->createResponse($event->getException())); + $exception = $event->getException(); + if (!$exception instanceof FlattenException) { + $exception = FlattenException::create($exception); + } + + $response = Response::create($handler->getHtml($exception), $exception->getStatusCode(), $exception->getHeaders())->setCharset(ini_get('default_charset')); + + $event->setResponse($response); } /** diff --git a/lib/silex/vendor/silex/silex/src/Silex/ExceptionListenerWrapper.php b/lib/silex/vendor/silex/silex/src/Silex/ExceptionListenerWrapper.php index fc0d2318d..e0d527b00 100644 --- a/lib/silex/vendor/silex/silex/src/Silex/ExceptionListenerWrapper.php +++ b/lib/silex/vendor/silex/silex/src/Silex/ExceptionListenerWrapper.php @@ -50,7 +50,7 @@ class ExceptionListenerWrapper $code = $exception instanceof HttpExceptionInterface ? $exception->getStatusCode() : 500; - $response = call_user_func($this->callback, $exception, $code); + $response = call_user_func($this->callback, $exception, $event->getRequest(), $code); $this->ensureResponse($response, $event); } diff --git a/lib/silex/vendor/silex/silex/src/Silex/Provider/AssetServiceProvider.php b/lib/silex/vendor/silex/silex/src/Silex/Provider/AssetServiceProvider.php new file mode 100644 index 000000000..fa6033066 --- /dev/null +++ b/lib/silex/vendor/silex/silex/src/Silex/Provider/AssetServiceProvider.php @@ -0,0 +1,97 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Silex\Provider; + +use Pimple\Container; +use Pimple\ServiceProviderInterface; +use Symfony\Component\Asset\Packages; +use Symfony\Component\Asset\Package; +use Symfony\Component\Asset\PathPackage; +use Symfony\Component\Asset\UrlPackage; +use Symfony\Component\Asset\Context\RequestStackContext; +use Symfony\Component\Asset\VersionStrategy\EmptyVersionStrategy; +use Symfony\Component\Asset\VersionStrategy\JsonManifestVersionStrategy; +use Symfony\Component\Asset\VersionStrategy\StaticVersionStrategy; + +/** + * Symfony Asset component Provider. + * + * @author Fabien Potencier + */ +class AssetServiceProvider implements ServiceProviderInterface +{ + public function register(Container $app) + { + $app['assets.packages'] = function ($app) { + $packages = array(); + foreach ($app['assets.named_packages'] as $name => $package) { + $version = $app['assets.strategy_factory'](isset($package['version']) ? $package['version'] : null, isset($package['version_format']) ? $package['version_format'] : null, isset($package['json_manifest_path']) ? $package['json_manifest_path'] : null, $name); + + $packages[$name] = $app['assets.package_factory'](isset($package['base_path']) ? $package['base_path'] : '', isset($package['base_urls']) ? $package['base_urls'] : array(), $version, $name); + } + + return new Packages($app['assets.default_package'], $packages); + }; + + $app['assets.default_package'] = function ($app) { + $version = $app['assets.strategy_factory']($app['assets.version'], $app['assets.version_format'], $app['assets.json_manifest_path'], 'default'); + + return $app['assets.package_factory']($app['assets.base_path'], $app['assets.base_urls'], $version, 'default'); + }; + + $app['assets.context'] = function ($app) { + return new RequestStackContext($app['request_stack']); + }; + + $app['assets.base_path'] = ''; + $app['assets.base_urls'] = array(); + $app['assets.version'] = null; + $app['assets.version_format'] = null; + $app['assets.json_manifest_path'] = null; + + $app['assets.named_packages'] = array(); + + // prototypes + + $app['assets.strategy_factory'] = $app->protect(function ($version, $format, $jsonManifestPath, $name) use ($app) { + if ($version && $jsonManifestPath) { + throw new \LogicException(sprintf('Asset package "%s" cannot have version and manifest.', $name)); + } + + if ($version) { + return new StaticVersionStrategy($version, $format); + } + + if ($jsonManifestPath) { + if (!class_exists('Symfony\Component\Asset\VersionStrategy\JsonManifestVersionStrategy')) { + throw new \RuntimeException('You must require symfony/asset >= 3.3 to use JSON manifest version strategy.'); + } + + return new JsonManifestVersionStrategy($jsonManifestPath); + } + + return new EmptyVersionStrategy(); + }); + + $app['assets.package_factory'] = $app->protect(function ($basePath, $baseUrls, $version, $name) use ($app) { + if ($basePath && $baseUrls) { + throw new \LogicException(sprintf('Asset package "%s" cannot have base URLs and base paths.', $name)); + } + + if (!$baseUrls) { + return new PathPackage($basePath, $version, $app['assets.context']); + } + + return new UrlPackage($baseUrls, $version, $app['assets.context']); + }); + } +} diff --git a/lib/silex/vendor/silex/silex/src/Silex/Provider/CsrfServiceProvider.php b/lib/silex/vendor/silex/silex/src/Silex/Provider/CsrfServiceProvider.php new file mode 100644 index 000000000..eb6e882df --- /dev/null +++ b/lib/silex/vendor/silex/silex/src/Silex/Provider/CsrfServiceProvider.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 Silex\Provider; + +use Pimple\Container; +use Pimple\ServiceProviderInterface; +use Symfony\Component\Security\Csrf\CsrfTokenManager; +use Symfony\Component\Security\Csrf\TokenGenerator\UriSafeTokenGenerator; +use Symfony\Component\Security\Csrf\TokenStorage\SessionTokenStorage; +use Symfony\Component\Security\Csrf\TokenStorage\NativeSessionTokenStorage; + +/** + * Symfony CSRF Security component Provider. + * + * @author Fabien Potencier + */ +class CsrfServiceProvider implements ServiceProviderInterface +{ + public function register(Container $app) + { + $app['csrf.token_manager'] = function ($app) { + return new CsrfTokenManager($app['csrf.token_generator'], $app['csrf.token_storage']); + }; + + $app['csrf.token_storage'] = function ($app) { + if (isset($app['session'])) { + return new SessionTokenStorage($app['session'], $app['csrf.session_namespace']); + } + + return new NativeSessionTokenStorage($app['csrf.session_namespace']); + }; + + $app['csrf.token_generator'] = function ($app) { + return new UriSafeTokenGenerator(); + }; + + $app['csrf.session_namespace'] = '_csrf'; + } +} diff --git a/lib/silex/vendor/silex/silex/src/Silex/Provider/DoctrineServiceProvider.php b/lib/silex/vendor/silex/silex/src/Silex/Provider/DoctrineServiceProvider.php index c4c62b061..9c71d5b79 100644 --- a/lib/silex/vendor/silex/silex/src/Silex/Provider/DoctrineServiceProvider.php +++ b/lib/silex/vendor/silex/silex/src/Silex/Provider/DoctrineServiceProvider.php @@ -11,8 +11,8 @@ namespace Silex\Provider; -use Silex\Application; -use Silex\ServiceProviderInterface; +use Pimple\Container; +use Pimple\ServiceProviderInterface; use Doctrine\DBAL\DriverManager; use Doctrine\DBAL\Configuration; use Doctrine\Common\EventManager; @@ -25,13 +25,13 @@ use Symfony\Bridge\Doctrine\Logger\DbalLogger; */ class DoctrineServiceProvider implements ServiceProviderInterface { - public function register(Application $app) + public function register(Container $app) { $app['db.default_options'] = array( - 'driver' => 'pdo_mysql', - 'dbname' => null, - 'host' => 'localhost', - 'user' => 'root', + 'driver' => 'pdo_mysql', + 'dbname' => null, + 'host' => 'localhost', + 'user' => 'root', 'password' => null, ); @@ -59,10 +59,10 @@ class DoctrineServiceProvider implements ServiceProviderInterface $app['dbs.options'] = $tmp; }); - $app['dbs'] = $app->share(function ($app) { + $app['dbs'] = function ($app) { $app['dbs.options.initializer'](); - $dbs = new \Pimple(); + $dbs = new Container(); foreach ($app['dbs.options'] as $name => $options) { if ($app['dbs.default'] === $name) { // we use shortcuts here in case the default has been overridden @@ -73,61 +73,57 @@ class DoctrineServiceProvider implements ServiceProviderInterface $manager = $app['dbs.event_manager'][$name]; } - $dbs[$name] = $dbs->share(function ($dbs) use ($options, $config, $manager) { + $dbs[$name] = function ($dbs) use ($options, $config, $manager) { return DriverManager::getConnection($options, $config, $manager); - }); + }; } return $dbs; - }); + }; - $app['dbs.config'] = $app->share(function ($app) { + $app['dbs.config'] = function ($app) { $app['dbs.options.initializer'](); - $configs = new \Pimple(); + $configs = new Container(); + $addLogger = isset($app['logger']) && null !== $app['logger'] && class_exists('Symfony\Bridge\Doctrine\Logger\DbalLogger'); foreach ($app['dbs.options'] as $name => $options) { $configs[$name] = new Configuration(); - - if (isset($app['logger']) && class_exists('Symfony\Bridge\Doctrine\Logger\DbalLogger')) { + if ($addLogger) { $configs[$name]->setSQLLogger(new DbalLogger($app['logger'], isset($app['stopwatch']) ? $app['stopwatch'] : null)); } } return $configs; - }); + }; - $app['dbs.event_manager'] = $app->share(function ($app) { + $app['dbs.event_manager'] = function ($app) { $app['dbs.options.initializer'](); - $managers = new \Pimple(); + $managers = new Container(); foreach ($app['dbs.options'] as $name => $options) { $managers[$name] = new EventManager(); } return $managers; - }); + }; // shortcuts for the "first" DB - $app['db'] = $app->share(function ($app) { + $app['db'] = function ($app) { $dbs = $app['dbs']; return $dbs[$app['dbs.default']]; - }); + }; - $app['db.config'] = $app->share(function ($app) { + $app['db.config'] = function ($app) { $dbs = $app['dbs.config']; return $dbs[$app['dbs.default']]; - }); + }; - $app['db.event_manager'] = $app->share(function ($app) { + $app['db.event_manager'] = function ($app) { $dbs = $app['dbs.event_manager']; return $dbs[$app['dbs.default']]; - }); - } - - public function boot(Application $app) - { + }; } } diff --git a/lib/silex/vendor/silex/silex/src/Silex/Provider/ExceptionHandlerServiceProvider.php b/lib/silex/vendor/silex/silex/src/Silex/Provider/ExceptionHandlerServiceProvider.php new file mode 100644 index 000000000..1c6f20286 --- /dev/null +++ b/lib/silex/vendor/silex/silex/src/Silex/Provider/ExceptionHandlerServiceProvider.php @@ -0,0 +1,32 @@ +addSubscriber($app['exception_handler']); + } + } +} diff --git a/lib/silex/vendor/silex/silex/src/Silex/Provider/Form/SilexFormExtension.php b/lib/silex/vendor/silex/silex/src/Silex/Provider/Form/SilexFormExtension.php new file mode 100644 index 000000000..12efbdf23 --- /dev/null +++ b/lib/silex/vendor/silex/silex/src/Silex/Provider/Form/SilexFormExtension.php @@ -0,0 +1,122 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Silex\Provider\Form; + +use Pimple\Container; +use Symfony\Component\Form\Exception\InvalidArgumentException; +use Symfony\Component\Form\FormExtensionInterface; +use Symfony\Component\Form\FormTypeGuesserChain; + +class SilexFormExtension implements FormExtensionInterface +{ + private $app; + private $types; + private $typeExtensions; + private $guessers; + private $guesserLoaded = false; + private $guesser; + + public function __construct(Container $app, array $types, array $typeExtensions, array $guessers) + { + $this->app = $app; + $this->setTypes($types); + $this->setTypeExtensions($typeExtensions); + $this->setGuessers($guessers); + } + + public function getType($name) + { + if (!isset($this->types[$name])) { + throw new InvalidArgumentException(sprintf('The type "%s" is not the name of a registered form type.', $name)); + } + if (!is_object($this->types[$name])) { + $this->types[$name] = $this->app[$this->types[$name]]; + } + + return $this->types[$name]; + } + + public function hasType($name) + { + return isset($this->types[$name]); + } + + public function getTypeExtensions($name) + { + return isset($this->typeExtensions[$name]) ? $this->typeExtensions[$name] : []; + } + + public function hasTypeExtensions($name) + { + return isset($this->typeExtensions[$name]); + } + + public function getTypeGuesser() + { + if (!$this->guesserLoaded) { + $this->guesserLoaded = true; + + if ($this->guessers) { + $guessers = []; + foreach ($this->guessers as $guesser) { + if (!is_object($guesser)) { + $guesser = $this->app[$guesser]; + } + $guessers[] = $guesser; + } + $this->guesser = new FormTypeGuesserChain($guessers); + } + } + + return $this->guesser; + } + + private function setTypes(array $types) + { + $this->types = []; + foreach ($types as $type) { + if (!is_object($type)) { + if (!isset($this->app[$type])) { + throw new InvalidArgumentException(sprintf('Invalid form type. The silex service "%s" does not exist.', $type)); + } + $this->types[$type] = $type; + } else { + $this->types[get_class($type)] = $type; + } + } + } + + private function setTypeExtensions(array $typeExtensions) + { + $this->typeExtensions = []; + foreach ($typeExtensions as $extension) { + if (!is_object($extension)) { + if (!isset($this->app[$extension])) { + throw new InvalidArgumentException(sprintf('Invalid form type extension. The silex service "%s" does not exist.', $extension)); + } + $extension = $this->app[$extension]; + } + $this->typeExtensions[$extension->getExtendedType()][] = $extension; + } + } + + private function setGuessers(array $guessers) + { + $this->guessers = []; + foreach ($guessers as $guesser) { + if (!is_object($guesser) && !isset($this->app[$guesser])) { + throw new InvalidArgumentException(sprintf('Invalid form type guesser. The silex service "%s" does not exist.', $guesser)); + } + $this->guessers[] = $guesser; + } + } +} diff --git a/lib/silex/vendor/silex/silex/src/Silex/Provider/FormServiceProvider.php b/lib/silex/vendor/silex/silex/src/Silex/Provider/FormServiceProvider.php index 25562e872..00841d0bf 100644 --- a/lib/silex/vendor/silex/silex/src/Silex/Provider/FormServiceProvider.php +++ b/lib/silex/vendor/silex/silex/src/Silex/Provider/FormServiceProvider.php @@ -11,14 +11,13 @@ namespace Silex\Provider; -use Silex\Application; -use Silex\ServiceProviderInterface; +use Pimple\Container; +use Pimple\ServiceProviderInterface; use Symfony\Component\Form\Extension\Csrf\CsrfExtension; -use Symfony\Component\Form\Extension\Csrf\CsrfProvider\DefaultCsrfProvider; -use Symfony\Component\Form\Extension\Csrf\CsrfProvider\SessionCsrfProvider; use Symfony\Component\Form\Extension\HttpFoundation\HttpFoundationExtension; use Symfony\Component\Form\Extension\Validator\ValidatorExtension as FormValidatorExtension; -use Symfony\Component\Form\Forms; +use Symfony\Component\Form\FormFactory; +use Symfony\Component\Form\FormRegistry; use Symfony\Component\Form\ResolvedFormTypeFactory; /** @@ -28,91 +27,63 @@ use Symfony\Component\Form\ResolvedFormTypeFactory; */ class FormServiceProvider implements ServiceProviderInterface { - public function register(Application $app) + public function register(Container $app) { - if (!class_exists('Locale') && !class_exists('Symfony\Component\Locale\Stub\StubLocale')) { - throw new \RuntimeException('You must either install the PHP intl extension or the Symfony Locale Component to use the Form extension.'); - } - if (!class_exists('Locale')) { - $r = new \ReflectionClass('Symfony\Component\Locale\Stub\StubLocale'); - $path = dirname(dirname($r->getFilename())).'/Resources/stubs'; - - require_once $path.'/functions.php'; - require_once $path.'/Collator.php'; - require_once $path.'/IntlDateFormatter.php'; - require_once $path.'/Locale.php'; - require_once $path.'/NumberFormatter.php'; + throw new \RuntimeException('You must either install the PHP intl extension or the Symfony Intl Component to use the Form extension.'); } - $app['form.secret'] = md5(__DIR__); - - $app['form.types'] = $app->share(function ($app) { + $app['form.types'] = function ($app) { return array(); - }); + }; - $app['form.type.extensions'] = $app->share(function ($app) { + $app['form.type.extensions'] = function ($app) { return array(); - }); + }; - $app['form.type.guessers'] = $app->share(function ($app) { + $app['form.type.guessers'] = function ($app) { return array(); - }); + }; - $app['form.extension.csrf'] = $app->share(function ($app) { + $app['form.extension.csrf'] = function ($app) { if (isset($app['translator'])) { - return new CsrfExtension($app['form.csrf_provider'], $app['translator']); + return new CsrfExtension($app['csrf.token_manager'], $app['translator']); } - return new CsrfExtension($app['form.csrf_provider']); - }); + return new CsrfExtension($app['csrf.token_manager']); + }; - $app['form.extensions'] = $app->share(function ($app) { + $app['form.extension.silex'] = function ($app) { + return new Form\SilexFormExtension($app, $app['form.types'], $app['form.type.extensions'], $app['form.type.guessers']); + }; + + $app['form.extensions'] = function ($app) { $extensions = array( - $app['form.extension.csrf'], new HttpFoundationExtension(), ); + if (isset($app['csrf.token_manager'])) { + $extensions[] = $app['form.extension.csrf']; + } + if (isset($app['validator'])) { $extensions[] = new FormValidatorExtension($app['validator']); - - if (isset($app['translator'])) { - $r = new \ReflectionClass('Symfony\Component\Form\Form'); - $file = dirname($r->getFilename()).'/Resources/translations/validators.'.$app['locale'].'.xlf'; - if (file_exists($file)) { - $app['translator']->addResource('xliff', $file, $app['locale'], 'validators'); - } - } } + $extensions[] = $app['form.extension.silex']; return $extensions; - }); + }; - $app['form.factory'] = $app->share(function ($app) { - return Forms::createFormFactoryBuilder() - ->addExtensions($app['form.extensions']) - ->addTypes($app['form.types']) - ->addTypeExtensions($app['form.type.extensions']) - ->addTypeGuessers($app['form.type.guessers']) - ->setResolvedTypeFactory($app['form.resolved_type_factory']) - ->getFormFactory() - ; - }); + $app['form.factory'] = function ($app) { + return new FormFactory($app['form.registry'], $app['form.resolved_type_factory']); + }; - $app['form.resolved_type_factory'] = $app->share(function ($app) { + $app['form.registry'] = function ($app) { + return new FormRegistry($app['form.extensions'], $app['form.resolved_type_factory']); + }; + + $app['form.resolved_type_factory'] = function ($app) { return new ResolvedFormTypeFactory(); - }); - - $app['form.csrf_provider'] = $app->share(function ($app) { - if (isset($app['session'])) { - return new SessionCsrfProvider($app['session'], $app['form.secret']); - } - - return new DefaultCsrfProvider($app['form.secret']); - }); - } - - public function boot(Application $app) - { + }; } } diff --git a/lib/silex/vendor/silex/silex/src/Silex/Provider/HttpCache/HttpCache.php b/lib/silex/vendor/silex/silex/src/Silex/Provider/HttpCache/HttpCache.php new file mode 100644 index 000000000..b0ebb5ccb --- /dev/null +++ b/lib/silex/vendor/silex/silex/src/Silex/Provider/HttpCache/HttpCache.php @@ -0,0 +1,39 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Silex\Provider\HttpCache; + +use Symfony\Component\HttpKernel\HttpCache\HttpCache as BaseHttpCache; +use Symfony\Component\HttpFoundation\Request; + +/** + * HTTP Cache extension to allow using the run() shortcut. + * + * @author Fabien Potencier + */ +class HttpCache extends BaseHttpCache +{ + /** + * Handles the Request and delivers the Response. + * + * @param Request $request The Request object + */ + public function run(Request $request = null) + { + if (null === $request) { + $request = Request::createFromGlobals(); + } + + $response = $this->handle($request); + $response->send(); + $this->terminate($request, $response); + } +} diff --git a/lib/silex/vendor/silex/silex/src/Silex/Provider/HttpCacheServiceProvider.php b/lib/silex/vendor/silex/silex/src/Silex/Provider/HttpCacheServiceProvider.php index 18217e9eb..8b3f37ee6 100644 --- a/lib/silex/vendor/silex/silex/src/Silex/Provider/HttpCacheServiceProvider.php +++ b/lib/silex/vendor/silex/silex/src/Silex/Provider/HttpCacheServiceProvider.php @@ -11,23 +11,25 @@ namespace Silex\Provider; -use Silex\Application; -use Silex\ServiceProviderInterface; -use Silex\HttpCache; +use Pimple\Container; +use Pimple\ServiceProviderInterface; +use Silex\Provider\HttpCache\HttpCache; +use Silex\Api\EventListenerProviderInterface; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\HttpKernel\HttpCache\Esi; use Symfony\Component\HttpKernel\HttpCache\Store; -use Symfony\Component\HttpKernel\EventListener\EsiListener; +use Symfony\Component\HttpKernel\EventListener\SurrogateListener; /** * Symfony HttpKernel component Provider for HTTP cache. * * @author Fabien Potencier */ -class HttpCacheServiceProvider implements ServiceProviderInterface +class HttpCacheServiceProvider implements ServiceProviderInterface, EventListenerProviderInterface { - public function register(Application $app) + public function register(Container $app) { - $app['http_cache'] = $app->share(function ($app) { + $app['http_cache'] = function ($app) { $app['http_cache.options'] = array_replace( array( 'debug' => $app['debug'], @@ -35,25 +37,25 @@ class HttpCacheServiceProvider implements ServiceProviderInterface ); return new HttpCache($app, $app['http_cache.store'], $app['http_cache.esi'], $app['http_cache.options']); - }); + }; - $app['http_cache.esi'] = $app->share(function ($app) { + $app['http_cache.esi'] = function ($app) { return new Esi(); - }); + }; - $app['http_cache.store'] = $app->share(function ($app) { + $app['http_cache.store'] = function ($app) { return new Store($app['http_cache.cache_dir']); - }); + }; - $app['http_cache.esi_listener'] = $app->share(function ($app) { - return new EsiListener($app['http_cache.esi']); - }); + $app['http_cache.esi_listener'] = function ($app) { + return new SurrogateListener($app['http_cache.esi']); + }; $app['http_cache.options'] = array(); } - public function boot(Application $app) + public function subscribe(Container $app, EventDispatcherInterface $dispatcher) { - $app['dispatcher']->addSubscriber($app['http_cache.esi_listener']); + $dispatcher->addSubscriber($app['http_cache.esi_listener']); } } diff --git a/lib/silex/vendor/silex/silex/src/Silex/Provider/HttpFragmentServiceProvider.php b/lib/silex/vendor/silex/silex/src/Silex/Provider/HttpFragmentServiceProvider.php index f72a97b62..fb1f4990a 100644 --- a/lib/silex/vendor/silex/silex/src/Silex/Provider/HttpFragmentServiceProvider.php +++ b/lib/silex/vendor/silex/silex/src/Silex/Provider/HttpFragmentServiceProvider.php @@ -11,67 +11,64 @@ namespace Silex\Provider; -use Silex\Application; -use Silex\ServiceProviderInterface; +use Pimple\Container; +use Pimple\ServiceProviderInterface; +use Silex\Api\EventListenerProviderInterface; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\HttpKernel\Fragment\FragmentHandler; use Symfony\Component\HttpKernel\Fragment\InlineFragmentRenderer; use Symfony\Component\HttpKernel\Fragment\EsiFragmentRenderer; use Symfony\Component\HttpKernel\Fragment\HIncludeFragmentRenderer; use Symfony\Component\HttpKernel\EventListener\FragmentListener; +use Symfony\Component\HttpKernel\Kernel; use Symfony\Component\HttpKernel\UriSigner; /** * HttpKernel Fragment integration for Silex. * - * This service provider requires Symfony 2.4+. - * * @author Fabien Potencier */ -class HttpFragmentServiceProvider implements ServiceProviderInterface +class HttpFragmentServiceProvider implements ServiceProviderInterface, EventListenerProviderInterface { - public function register(Application $app) + public function register(Container $app) { - if (!class_exists('Symfony\Component\HttpFoundation\RequestStack')) { - throw new \LogicException('The HTTP Fragment service provider only works with Symfony 2.4+.'); - } + $app['fragment.handler'] = function ($app) { + return new FragmentHandler($app['request_stack'], $app['fragment.renderers'], $app['debug']); + }; - $app['fragment.handler'] = $app->share(function ($app) { - return new FragmentHandler($app['fragment.renderers'], $app['debug'], $app['request_stack']); - }); - - $app['fragment.renderer.inline'] = $app->share(function ($app) { + $app['fragment.renderer.inline'] = function ($app) { $renderer = new InlineFragmentRenderer($app['kernel'], $app['dispatcher']); $renderer->setFragmentPath($app['fragment.path']); return $renderer; - }); + }; - $app['fragment.renderer.hinclude'] = $app->share(function ($app) { + $app['fragment.renderer.hinclude'] = function ($app) { $renderer = new HIncludeFragmentRenderer(null, $app['uri_signer'], $app['fragment.renderer.hinclude.global_template'], $app['charset']); $renderer->setFragmentPath($app['fragment.path']); return $renderer; - }); + }; - $app['fragment.renderer.esi'] = $app->share(function ($app) { - $renderer = new EsiFragmentRenderer($app['http_cache.esi'], $app['fragment.renderer.inline']); + $app['fragment.renderer.esi'] = function ($app) { + $renderer = new EsiFragmentRenderer($app['http_cache.esi'], $app['fragment.renderer.inline'], $app['uri_signer']); $renderer->setFragmentPath($app['fragment.path']); return $renderer; - }); + }; - $app['fragment.listener'] = $app->share(function ($app) { + $app['fragment.listener'] = function ($app) { return new FragmentListener($app['uri_signer'], $app['fragment.path']); - }); + }; - $app['uri_signer'] = $app->share(function ($app) { + $app['uri_signer'] = function ($app) { return new UriSigner($app['uri_signer.secret']); - }); + }; $app['uri_signer.secret'] = md5(__DIR__); $app['fragment.path'] = '/_fragment'; $app['fragment.renderer.hinclude.global_template'] = null; - $app['fragment.renderers'] = $app->share(function ($app) { + $app['fragment.renderers'] = function ($app) { $renderers = array($app['fragment.renderer.inline'], $app['fragment.renderer.hinclude']); if (isset($app['http_cache.esi'])) { @@ -79,11 +76,11 @@ class HttpFragmentServiceProvider implements ServiceProviderInterface } return $renderers; - }); + }; } - public function boot(Application $app) + public function subscribe(Container $app, EventDispatcherInterface $dispatcher) { - $app['dispatcher']->addSubscriber($app['fragment.listener']); + $dispatcher->addSubscriber($app['fragment.listener']); } } diff --git a/lib/silex/vendor/silex/silex/src/Silex/Provider/HttpKernelServiceProvider.php b/lib/silex/vendor/silex/silex/src/Silex/Provider/HttpKernelServiceProvider.php new file mode 100644 index 000000000..86f155fe6 --- /dev/null +++ b/lib/silex/vendor/silex/silex/src/Silex/Provider/HttpKernelServiceProvider.php @@ -0,0 +1,101 @@ += 30100) { + return new SfControllerResolver($app['logger']); + } + + return new ControllerResolver($app, $app['logger']); + }; + + if (Kernel::VERSION_ID >= 30100) { + $app['argument_metadata_factory'] = function ($app) { + return new ArgumentMetadataFactory(); + }; + $app['argument_value_resolvers'] = function ($app) { + if (Kernel::VERSION_ID < 30200) { + return array( + new AppArgumentValueResolver($app), + new RequestAttributeValueResolver(), + new RequestValueResolver(), + new DefaultValueResolver(), + new VariadicValueResolver(), + ); + } + + return array_merge(array(new AppArgumentValueResolver($app)), ArgumentResolver::getDefaultArgumentValueResolvers()); + }; + } + + $app['argument_resolver'] = function ($app) { + if (Kernel::VERSION_ID >= 30100) { + return new ArgumentResolver($app['argument_metadata_factory'], $app['argument_value_resolvers']); + } + }; + + $app['kernel'] = function ($app) { + return new HttpKernel($app['dispatcher'], $app['resolver'], $app['request_stack'], $app['argument_resolver']); + }; + + $app['request_stack'] = function () { + return new RequestStack(); + }; + + $app['dispatcher'] = function () { + return new EventDispatcher(); + }; + + $app['callback_resolver'] = function ($app) { + return new CallbackResolver($app); + }; + } + + /** + * {@inheritdoc} + */ + public function subscribe(Container $app, EventDispatcherInterface $dispatcher) + { + $dispatcher->addSubscriber(new ResponseListener($app['charset'])); + $dispatcher->addSubscriber(new MiddlewareListener($app)); + $dispatcher->addSubscriber(new ConverterListener($app['routes'], $app['callback_resolver'])); + $dispatcher->addSubscriber(new StringToResponseListener()); + + if (class_exists(HttpHeaderSerializer::class)) { + $dispatcher->addSubscriber(new AddLinkHeaderListener()); + } + } +} diff --git a/lib/silex/vendor/silex/silex/src/Silex/Provider/LICENSE b/lib/silex/vendor/silex/silex/src/Silex/Provider/LICENSE new file mode 100644 index 000000000..bc6ad0497 --- /dev/null +++ b/lib/silex/vendor/silex/silex/src/Silex/Provider/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2010-2015 Fabien Potencier + +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/lib/silex/vendor/silex/silex/src/Silex/Provider/Locale/LocaleListener.php b/lib/silex/vendor/silex/silex/src/Silex/Provider/Locale/LocaleListener.php new file mode 100644 index 000000000..d5002640e --- /dev/null +++ b/lib/silex/vendor/silex/silex/src/Silex/Provider/Locale/LocaleListener.php @@ -0,0 +1,84 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Silex\Provider\Locale; + +use Pimple\Container; +use Symfony\Component\HttpKernel\Event\GetResponseEvent; +use Symfony\Component\HttpKernel\Event\FinishRequestEvent; +use Symfony\Component\HttpKernel\KernelEvents; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\RequestStack; +use Symfony\Component\Routing\RequestContext; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; + +/** + * Initializes the locale based on the current request. + * + * @author Fabien Potencier + * @author Jérôme Tamarelle + */ +class LocaleListener implements EventSubscriberInterface +{ + private $app; + private $defaultLocale; + private $requestStack; + private $requestContext; + + public function __construct(Container $app, $defaultLocale = 'en', RequestStack $requestStack, RequestContext $requestContext = null) + { + $this->app = $app; + $this->defaultLocale = $defaultLocale; + $this->requestStack = $requestStack; + $this->requestContext = $requestContext; + } + + public function onKernelRequest(GetResponseEvent $event) + { + $request = $event->getRequest(); + $request->setDefaultLocale($this->defaultLocale); + + $this->setLocale($request); + $this->setRouterContext($request); + + $this->app['locale'] = $request->getLocale(); + } + + public function onKernelFinishRequest(FinishRequestEvent $event) + { + if (null !== $parentRequest = $this->requestStack->getParentRequest()) { + $this->setRouterContext($parentRequest); + } + } + + private function setLocale(Request $request) + { + if ($locale = $request->attributes->get('_locale')) { + $request->setLocale($locale); + } + } + + private function setRouterContext(Request $request) + { + if (null !== $this->requestContext) { + $this->requestContext->setParameter('_locale', $request->getLocale()); + } + } + + public static function getSubscribedEvents() + { + return array( + // must be registered after the Router to have access to the _locale + KernelEvents::REQUEST => array(array('onKernelRequest', 16)), + KernelEvents::FINISH_REQUEST => array(array('onKernelFinishRequest', 0)), + ); + } +} diff --git a/lib/silex/vendor/silex/silex/src/Silex/Provider/LocaleServiceProvider.php b/lib/silex/vendor/silex/silex/src/Silex/Provider/LocaleServiceProvider.php new file mode 100644 index 000000000..ddea81bfe --- /dev/null +++ b/lib/silex/vendor/silex/silex/src/Silex/Provider/LocaleServiceProvider.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 Silex\Provider; + +use Pimple\Container; +use Pimple\ServiceProviderInterface; +use Silex\Api\EventListenerProviderInterface; +use Silex\Provider\Locale\LocaleListener; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; + +/** + * Locale Provider. + * + * @author Fabien Potencier + */ +class LocaleServiceProvider implements ServiceProviderInterface, EventListenerProviderInterface +{ + public function register(Container $app) + { + $app['locale.listener'] = function ($app) { + return new LocaleListener($app, $app['locale'], $app['request_stack'], isset($app['request_context']) ? $app['request_context'] : null); + }; + + $app['locale'] = 'en'; + } + + public function subscribe(Container $app, EventDispatcherInterface $dispatcher) + { + $dispatcher->addSubscriber($app['locale.listener']); + } +} diff --git a/lib/silex/vendor/silex/silex/src/Silex/Provider/MonologServiceProvider.php b/lib/silex/vendor/silex/silex/src/Silex/Provider/MonologServiceProvider.php index acb21ef5b..f8cba4e2c 100644 --- a/lib/silex/vendor/silex/silex/src/Silex/Provider/MonologServiceProvider.php +++ b/lib/silex/vendor/silex/silex/src/Silex/Provider/MonologServiceProvider.php @@ -11,11 +11,17 @@ namespace Silex\Provider; +use Pimple\Container; +use Pimple\ServiceProviderInterface; +use Monolog\Formatter\LineFormatter; use Monolog\Logger; -use Monolog\Handler\StreamHandler; +use Monolog\Handler; +use Monolog\ErrorHandler; use Silex\Application; -use Silex\ServiceProviderInterface; +use Silex\Api\BootableProviderInterface; use Symfony\Bridge\Monolog\Handler\DebugHandler; +use Symfony\Bridge\Monolog\Handler\FingersCrossed\NotFoundActivationStrategy; +use Symfony\Bridge\Monolog\Processor\DebugProcessor; use Silex\EventListener\LogListener; /** @@ -23,9 +29,9 @@ use Silex\EventListener\LogListener; * * @author Fabien Potencier */ -class MonologServiceProvider implements ServiceProviderInterface +class MonologServiceProvider implements ServiceProviderInterface, BootableProviderInterface { - public function register(Application $app) + public function register(Container $app) { $app['logger'] = function () use ($app) { return $app['monolog']; @@ -37,43 +43,85 @@ class MonologServiceProvider implements ServiceProviderInterface return new DebugHandler($level); }; + + if (isset($app['request_stack'])) { + $app['monolog.not_found_activation_strategy'] = function () use ($app) { + return new NotFoundActivationStrategy($app['request_stack'], array('^/'), $app['monolog.level']); + }; + } } $app['monolog.logger.class'] = $bridge ? 'Symfony\Bridge\Monolog\Logger' : 'Monolog\Logger'; - $app['monolog'] = $app->share(function ($app) { + $app['monolog'] = function ($app) use ($bridge) { $log = new $app['monolog.logger.class']($app['monolog.name']); - $log->pushHandler($app['monolog.handler']); + $handler = new Handler\GroupHandler($app['monolog.handlers']); + if (isset($app['monolog.not_found_activation_strategy'])) { + $handler = new Handler\FingersCrossedHandler($handler, $app['monolog.not_found_activation_strategy']); + } - if ($app['debug'] && isset($app['monolog.handler.debug'])) { - $log->pushHandler($app['monolog.handler.debug']); + $log->pushHandler($handler); + + if ($app['debug'] && $bridge) { + if (class_exists(DebugProcessor::class)) { + $log->pushProcessor(new DebugProcessor()); + } else { + $log->pushHandler($app['monolog.handler.debug']); + } } return $log; - }); + }; - $app['monolog.handler'] = function () use ($app) { + $app['monolog.formatter'] = function () { + return new LineFormatter(); + }; + + $app['monolog.handler'] = $defaultHandler = function () use ($app) { $level = MonologServiceProvider::translateLevel($app['monolog.level']); - return new StreamHandler($app['monolog.logfile'], $level, $app['monolog.bubble'], $app['monolog.permission']); + $handler = new Handler\StreamHandler($app['monolog.logfile'], $level, $app['monolog.bubble'], $app['monolog.permission']); + $handler->setFormatter($app['monolog.formatter']); + + return $handler; + }; + + $app['monolog.handlers'] = function () use ($app, $defaultHandler) { + $handlers = array(); + + // enables the default handler if a logfile was set or the monolog.handler service was redefined + if ($app['monolog.logfile'] || $defaultHandler !== $app->raw('monolog.handler')) { + $handlers[] = $app['monolog.handler']; + } + + return $handlers; }; $app['monolog.level'] = function () { return Logger::DEBUG; }; - $app['monolog.listener'] = $app->share(function () use ($app) { - return new LogListener($app['logger']); - }); + $app['monolog.listener'] = function () use ($app) { + return new LogListener($app['logger'], $app['monolog.exception.logger_filter']); + }; - $app['monolog.name'] = 'myapp'; + $app['monolog.name'] = 'app'; $app['monolog.bubble'] = true; $app['monolog.permission'] = null; + $app['monolog.exception.logger_filter'] = null; + $app['monolog.logfile'] = null; + $app['monolog.use_error_handler'] = function ($app) { + return !$app['debug']; + }; } public function boot(Application $app) { + if ($app['monolog.use_error_handler']) { + ErrorHandler::register($app['monolog']); + } + if (isset($app['monolog.listener'])) { $app['dispatcher']->addSubscriber($app['monolog.listener']); } diff --git a/lib/silex/vendor/silex/silex/src/Silex/Provider/RememberMeServiceProvider.php b/lib/silex/vendor/silex/silex/src/Silex/Provider/RememberMeServiceProvider.php index 96f512c7d..766631c5a 100644 --- a/lib/silex/vendor/silex/silex/src/Silex/Provider/RememberMeServiceProvider.php +++ b/lib/silex/vendor/silex/silex/src/Silex/Provider/RememberMeServiceProvider.php @@ -11,25 +11,31 @@ namespace Silex\Provider; -use Silex\Application; -use Silex\ServiceProviderInterface; +use Pimple\Container; +use Pimple\ServiceProviderInterface; +use Silex\Api\EventListenerProviderInterface; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\Security\Core\Authentication\Provider\RememberMeAuthenticationProvider; use Symfony\Component\Security\Http\Firewall\RememberMeListener; use Symfony\Component\Security\Http\RememberMe\TokenBasedRememberMeServices; use Symfony\Component\Security\Http\RememberMe\ResponseListener; /** - * Remember-me authentication for the SecurityServiceProvider + * Remember-me authentication for the SecurityServiceProvider. * * @author Jérôme Tamarelle */ -class RememberMeServiceProvider implements ServiceProviderInterface +class RememberMeServiceProvider implements ServiceProviderInterface, EventListenerProviderInterface { - public function register(Application $app) + public function register(Container $app) { - $app['security.remember_me.response_listener'] = $app->share(function () { + $app['security.remember_me.response_listener'] = function ($app) { + if (!isset($app['security.token_storage'])) { + throw new \LogicException('You must register the SecurityServiceProvider to use the RememberMeServiceProvider'); + } + return new ResponseListener(); - }); + }; $app['security.authentication_listener.factory.remember_me'] = $app->protect(function ($name, $options) use ($app) { if (empty($options['key'])) { @@ -57,26 +63,26 @@ class RememberMeServiceProvider implements ServiceProviderInterface }); $app['security.remember_me.service._proto'] = $app->protect(function ($providerKey, $options) use ($app) { - return $app->share(function () use ($providerKey, $options, $app) { + return function () use ($providerKey, $options, $app) { $options = array_replace(array( - 'name' => 'REMEMBERME', - 'lifetime' => 31536000, - 'path' => '/', - 'domain' => null, - 'secure' => false, - 'httponly' => true, - 'always_remember_me' => false, + 'name' => 'REMEMBERME', + 'lifetime' => 31536000, + 'path' => '/', + 'domain' => null, + 'secure' => false, + 'httponly' => true, + 'always_remember_me' => false, 'remember_me_parameter' => '_remember_me', ), $options); return new TokenBasedRememberMeServices(array($app['security.user_provider.'.$providerKey]), $options['key'], $providerKey, $options, $app['logger']); - }); + }; }); $app['security.authentication_listener.remember_me._proto'] = $app->protect(function ($providerKey) use ($app) { - return $app->share(function () use ($app, $providerKey) { + return function () use ($app, $providerKey) { $listener = new RememberMeListener( - $app['security'], + $app['security.token_storage'], $app['security.remember_me.service.'.$providerKey], $app['security.authentication_manager'], $app['logger'], @@ -84,22 +90,18 @@ class RememberMeServiceProvider implements ServiceProviderInterface ); return $listener; - }); + }; }); $app['security.authentication_provider.remember_me._proto'] = $app->protect(function ($name, $options) use ($app) { - return $app->share(function () use ($app, $name, $options) { + return function () use ($app, $name, $options) { return new RememberMeAuthenticationProvider($app['security.user_checker'], $options['key'], $name); - }); + }; }); } - public function boot(Application $app) + public function subscribe(Container $app, EventDispatcherInterface $dispatcher) { - if (!isset($app['security'])) { - throw new \LogicException('You must register the SecurityServiceProvider to use the RememberMeServiceProvider'); - } - - $app['dispatcher']->addSubscriber($app['security.remember_me.response_listener']); + $dispatcher->addSubscriber($app['security.remember_me.response_listener']); } } diff --git a/lib/silex/vendor/silex/silex/src/Silex/Provider/Routing/LazyRequestMatcher.php b/lib/silex/vendor/silex/silex/src/Silex/Provider/Routing/LazyRequestMatcher.php new file mode 100644 index 000000000..6837c79ab --- /dev/null +++ b/lib/silex/vendor/silex/silex/src/Silex/Provider/Routing/LazyRequestMatcher.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 Silex\Provider\Routing; + +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\Routing\Matcher\RequestMatcherInterface; +use Symfony\Component\Routing\Matcher\UrlMatcherInterface; + +/** + * Implements a lazy UrlMatcher. + * + * @author Igor Wiedler + * @author Jérôme Tamarelle + */ +class LazyRequestMatcher implements RequestMatcherInterface +{ + private $factory; + + public function __construct(\Closure $factory) + { + $this->factory = $factory; + } + + /** + * Returns the corresponding RequestMatcherInterface instance. + * + * @return UrlMatcherInterface + */ + public function getRequestMatcher() + { + $matcher = call_user_func($this->factory); + if (!$matcher instanceof RequestMatcherInterface) { + throw new \LogicException("Factory supplied to LazyRequestMatcher must return implementation of Symfony\Component\Routing\RequestMatcherInterface."); + } + + return $matcher; + } + + /** + * {@inheritdoc} + */ + public function matchRequest(Request $request) + { + return $this->getRequestMatcher()->matchRequest($request); + } +} diff --git a/lib/silex/vendor/silex/silex/src/Silex/Provider/Routing/RedirectableUrlMatcher.php b/lib/silex/vendor/silex/silex/src/Silex/Provider/Routing/RedirectableUrlMatcher.php new file mode 100644 index 000000000..8b4a4dae9 --- /dev/null +++ b/lib/silex/vendor/silex/silex/src/Silex/Provider/Routing/RedirectableUrlMatcher.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 Silex\Provider\Routing; + +use Symfony\Component\HttpFoundation\RedirectResponse; +use Symfony\Component\Routing\Matcher\RedirectableUrlMatcher as BaseRedirectableUrlMatcher; + +/** + * Implements the RedirectableUrlMatcherInterface for Silex. + * + * @author Fabien Potencier + */ +class RedirectableUrlMatcher extends BaseRedirectableUrlMatcher +{ + /** + * {@inheritdoc} + */ + public function redirect($path, $route, $scheme = null) + { + $url = $this->context->getBaseUrl().$path; + $query = $this->context->getQueryString() ?: ''; + + if ($query !== '') { + $url .= '?'.$query; + } + + if ($this->context->getHost()) { + if ($scheme) { + $port = ''; + if ('http' === $scheme && 80 != $this->context->getHttpPort()) { + $port = ':'.$this->context->getHttpPort(); + } elseif ('https' === $scheme && 443 != $this->context->getHttpsPort()) { + $port = ':'.$this->context->getHttpsPort(); + } + + $url = $scheme.'://'.$this->context->getHost().$port.$url; + } + } + + return array( + '_controller' => function ($url) { return new RedirectResponse($url, 301); }, + '_route' => $route, + 'url' => $url, + ); + } +} diff --git a/lib/silex/vendor/silex/silex/src/Silex/Provider/RoutingServiceProvider.php b/lib/silex/vendor/silex/silex/src/Silex/Provider/RoutingServiceProvider.php new file mode 100644 index 000000000..d040ba0d6 --- /dev/null +++ b/lib/silex/vendor/silex/silex/src/Silex/Provider/RoutingServiceProvider.php @@ -0,0 +1,87 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Silex\Provider; + +use Pimple\Container; +use Pimple\ServiceProviderInterface; +use Silex\ControllerCollection; +use Silex\Api\EventListenerProviderInterface; +use Silex\Provider\Routing\RedirectableUrlMatcher; +use Silex\Provider\Routing\LazyRequestMatcher; +use Symfony\Component\Routing\RouteCollection; +use Symfony\Component\Routing\Generator\UrlGenerator; +use Symfony\Component\Routing\RequestContext; +use Symfony\Component\HttpKernel\EventListener\RouterListener; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; + +/** + * Symfony Routing component Provider. + * + * @author Fabien Potencier + */ +class RoutingServiceProvider implements ServiceProviderInterface, EventListenerProviderInterface +{ + public function register(Container $app) + { + $app['route_class'] = 'Silex\\Route'; + + $app['route_factory'] = $app->factory(function ($app) { + return new $app['route_class'](); + }); + + $app['routes_factory'] = $app->factory(function () { + return new RouteCollection(); + }); + + $app['routes'] = function ($app) { + return $app['routes_factory']; + }; + $app['url_generator'] = function ($app) { + return new UrlGenerator($app['routes'], $app['request_context']); + }; + + $app['request_matcher'] = function ($app) { + return new RedirectableUrlMatcher($app['routes'], $app['request_context']); + }; + + $app['request_context'] = function ($app) { + $context = new RequestContext(); + + $context->setHttpPort(isset($app['request.http_port']) ? $app['request.http_port'] : 80); + $context->setHttpsPort(isset($app['request.https_port']) ? $app['request.https_port'] : 443); + + return $context; + }; + + $app['controllers'] = function ($app) { + return $app['controllers_factory']; + }; + + $controllers_factory = function () use ($app, &$controllers_factory) { + return new ControllerCollection($app['route_factory'], $app['routes_factory'], $controllers_factory); + }; + $app['controllers_factory'] = $app->factory($controllers_factory); + + $app['routing.listener'] = function ($app) { + $urlMatcher = new LazyRequestMatcher(function () use ($app) { + return $app['request_matcher']; + }); + + return new RouterListener($urlMatcher, $app['request_stack'], $app['request_context'], $app['logger']); + }; + } + + public function subscribe(Container $app, EventDispatcherInterface $dispatcher) + { + $dispatcher->addSubscriber($app['routing.listener']); + } +} diff --git a/lib/silex/vendor/silex/silex/src/Silex/Provider/SecurityServiceProvider.php b/lib/silex/vendor/silex/silex/src/Silex/Provider/SecurityServiceProvider.php index d8399047d..06d2e40d1 100644 --- a/lib/silex/vendor/silex/silex/src/Silex/Provider/SecurityServiceProvider.php +++ b/lib/silex/vendor/silex/silex/src/Silex/Provider/SecurityServiceProvider.php @@ -11,22 +11,30 @@ namespace Silex\Provider; +use Pimple\Container; +use Pimple\ServiceProviderInterface; use Silex\Application; -use Silex\ServiceProviderInterface; +use Silex\Api\BootableProviderInterface; +use Silex\Api\ControllerProviderInterface; +use Silex\Api\EventListenerProviderInterface; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\HttpFoundation\RequestMatcher; use Symfony\Component\HttpFoundation\Request; -use Symfony\Component\Security\Core\SecurityContext; -use Symfony\Component\Security\Core\SecurityContextInterface; +use Symfony\Component\Security\Core\Security; use Symfony\Component\Security\Core\User\UserChecker; use Symfony\Component\Security\Core\User\InMemoryUserProvider; use Symfony\Component\Security\Core\Encoder\EncoderFactory; use Symfony\Component\Security\Core\Encoder\MessageDigestPasswordEncoder; +use Symfony\Component\Security\Core\Encoder\BCryptPasswordEncoder; +use Symfony\Component\Security\Core\Encoder\Pbkdf2PasswordEncoder; use Symfony\Component\Security\Core\Authentication\Provider\DaoAuthenticationProvider; use Symfony\Component\Security\Core\Authentication\Provider\AnonymousAuthenticationProvider; use Symfony\Component\Security\Core\Authentication\AuthenticationProviderManager; use Symfony\Component\Security\Core\Authentication\AuthenticationTrustResolver; use Symfony\Component\Security\Http\Authentication\DefaultAuthenticationSuccessHandler; use Symfony\Component\Security\Http\Authentication\DefaultAuthenticationFailureHandler; +use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorage; +use Symfony\Component\Security\Core\Authorization\AuthorizationChecker; use Symfony\Component\Security\Core\Authorization\Voter\RoleHierarchyVoter; use Symfony\Component\Security\Core\Authorization\Voter\AuthenticatedVoter; use Symfony\Component\Security\Core\Authorization\AccessDecisionManager; @@ -51,17 +59,20 @@ use Symfony\Component\Security\Http\Logout\SessionLogoutHandler; use Symfony\Component\Security\Http\Logout\DefaultLogoutSuccessHandler; use Symfony\Component\Security\Http\AccessMap; use Symfony\Component\Security\Http\HttpUtils; +use Symfony\Component\Security\Guard\GuardAuthenticatorHandler; +use Symfony\Component\Security\Guard\Firewall\GuardAuthenticationListener; +use Symfony\Component\Security\Guard\Provider\GuardAuthenticationProvider; /** * Symfony Security component Provider. * * @author Fabien Potencier */ -class SecurityServiceProvider implements ServiceProviderInterface +class SecurityServiceProvider implements ServiceProviderInterface, EventListenerProviderInterface, ControllerProviderInterface, BootableProviderInterface { protected $fakeRoutes; - public function register(Application $app) + public function register(Container $app) { // used to register routes for login_check and logout $this->fakeRoutes = array(); @@ -71,63 +82,98 @@ class SecurityServiceProvider implements ServiceProviderInterface $app['security.role_hierarchy'] = array(); $app['security.access_rules'] = array(); $app['security.hide_user_not_found'] = true; + $app['security.encoder.bcrypt.cost'] = 13; - $app['security'] = $app->share(function ($app) { - return new SecurityContext($app['security.authentication_manager'], $app['security.access_manager']); + $app['security.authorization_checker'] = function ($app) { + return new AuthorizationChecker($app['security.token_storage'], $app['security.authentication_manager'], $app['security.access_manager']); + }; + + $app['security.token_storage'] = function ($app) { + return new TokenStorage(); + }; + + $app['user'] = $app->factory(function ($app) { + if (null === $token = $app['security.token_storage']->getToken()) { + return; + } + + if (!is_object($user = $token->getUser())) { + return; + } + + return $user; }); - $app['security.authentication_manager'] = $app->share(function ($app) { + $app['security.authentication_manager'] = function ($app) { $manager = new AuthenticationProviderManager($app['security.authentication_providers']); $manager->setEventDispatcher($app['dispatcher']); return $manager; - }); + }; // by default, all users use the digest encoder - $app['security.encoder_factory'] = $app->share(function ($app) { + $app['security.encoder_factory'] = function ($app) { return new EncoderFactory(array( - 'Symfony\Component\Security\Core\User\UserInterface' => $app['security.encoder.digest'], + 'Symfony\Component\Security\Core\User\UserInterface' => $app['security.default_encoder'], )); - }); + }; - $app['security.encoder.digest'] = $app->share(function ($app) { + // by default, all users use the BCrypt encoder + $app['security.default_encoder'] = function ($app) { + return $app['security.encoder.bcrypt']; + }; + + $app['security.encoder.digest'] = function ($app) { return new MessageDigestPasswordEncoder(); - }); + }; - $app['security.user_checker'] = $app->share(function ($app) { + $app['security.encoder.bcrypt'] = function ($app) { + return new BCryptPasswordEncoder($app['security.encoder.bcrypt.cost']); + }; + + $app['security.encoder.pbkdf2'] = function ($app) { + return new Pbkdf2PasswordEncoder(); + }; + + $app['security.user_checker'] = function ($app) { return new UserChecker(); - }); + }; - $app['security.access_manager'] = $app->share(function ($app) { + $app['security.access_manager'] = function ($app) { return new AccessDecisionManager($app['security.voters']); - }); + }; - $app['security.voters'] = $app->share(function ($app) { + $app['security.voters'] = function ($app) { return array( new RoleHierarchyVoter(new RoleHierarchy($app['security.role_hierarchy'])), new AuthenticatedVoter($app['security.trust_resolver']), ); - }); + }; - $app['security.firewall'] = $app->share(function ($app) { + $app['security.firewall'] = function ($app) { return new Firewall($app['security.firewall_map'], $app['dispatcher']); - }); + }; - $app['security.channel_listener'] = $app->share(function ($app) { + $app['security.channel_listener'] = function ($app) { return new ChannelListener( $app['security.access_map'], - new RetryAuthenticationEntryPoint($app['request.http_port'], $app['request.https_port']), + new RetryAuthenticationEntryPoint( + isset($app['request.http_port']) ? $app['request.http_port'] : 80, + isset($app['request.https_port']) ? $app['request.https_port'] : 443 + ), $app['logger'] ); - }); + }; // generate the build-in authentication factories - foreach (array('logout', 'pre_auth', 'form', 'http', 'remember_me', 'anonymous') as $type) { + foreach (array('logout', 'pre_auth', 'guard', 'form', 'http', 'remember_me', 'anonymous') as $type) { $entryPoint = null; if ('http' === $type) { $entryPoint = 'http'; } elseif ('form' === $type) { $entryPoint = 'form'; + } elseif ('guard' === $type) { + $entryPoint = 'guard'; } $app['security.authentication_listener.factory.'.$type] = $app->protect(function ($name, $options) use ($type, $app, $entryPoint) { @@ -139,9 +185,14 @@ class SecurityServiceProvider implements ServiceProviderInterface $app['security.authentication_listener.'.$name.'.'.$type] = $app['security.authentication_listener.'.$type.'._proto']($name, $options); } - $provider = 'anonymous' === $type ? 'anonymous' : 'dao'; + $provider = 'dao'; + if ('anonymous' === $type) { + $provider = 'anonymous'; + } elseif ('guard' === $type) { + $provider = 'guard'; + } if (!isset($app['security.authentication_provider.'.$name.'.'.$provider])) { - $app['security.authentication_provider.'.$name.'.'.$provider] = $app['security.authentication_provider.'.$provider.'._proto']($name); + $app['security.authentication_provider.'.$name.'.'.$provider] = $app['security.authentication_provider.'.$provider.'._proto']($name, $options); } return array( @@ -153,8 +204,8 @@ class SecurityServiceProvider implements ServiceProviderInterface }); } - $app['security.firewall_map'] = $app->share(function ($app) { - $positions = array('logout', 'pre_auth', 'form', 'http', 'remember_me', 'anonymous'); + $app['security.firewall_map'] = function ($app) { + $positions = array('logout', 'pre_auth', 'guard', 'form', 'http', 'remember_me', 'anonymous'); $providers = array(); $configs = array(); foreach ($app['security.firewalls'] as $name => $firewall) { @@ -163,10 +214,11 @@ class SecurityServiceProvider implements ServiceProviderInterface $users = isset($firewall['users']) ? $firewall['users'] : array(); $security = isset($firewall['security']) ? (bool) $firewall['security'] : true; $stateless = isset($firewall['stateless']) ? (bool) $firewall['stateless'] : false; - unset($firewall['pattern'], $firewall['users'], $firewall['security'], $firewall['stateless']); - + $context = isset($firewall['context']) ? $firewall['context'] : $name; + $hosts = isset($firewall['hosts']) ? $firewall['hosts'] : null; + $methods = isset($firewall['methods']) ? $firewall['methods'] : null; + unset($firewall['pattern'], $firewall['users'], $firewall['security'], $firewall['stateless'], $firewall['context'], $firewall['methods'], $firewall['hosts']); $protected = false === $security ? false : count($firewall); - $listeners = array('security.channel_listener'); if ($protected) { @@ -179,7 +231,7 @@ class SecurityServiceProvider implements ServiceProviderInterface } if (false === $stateless) { - $listeners[] = 'security.context_listener.'.$name; + $listeners[] = 'security.context_listener.'.$context; } $factories = array(); @@ -205,6 +257,8 @@ class SecurityServiceProvider implements ServiceProviderInterface throw new \LogicException(sprintf('The "%s" authentication entry is not registered.', $type)); } + $options['stateless'] = $stateless; + list($providerId, $listenerId, $entryPointId, $position) = $app['security.authentication_listener.factory.'.$type]($name, $options); if (null !== $entryPointId) { @@ -230,14 +284,24 @@ class SecurityServiceProvider implements ServiceProviderInterface } if (!isset($app['security.exception_listener.'.$name])) { - if (null == $entryPoint) { + if (null === $entryPoint) { $app[$entryPoint = 'security.entry_point.'.$name.'.form'] = $app['security.entry_point.form._proto']($name, array()); } - $app['security.exception_listener.'.$name] = $app['security.exception_listener._proto']($entryPoint, $name); + $accessDeniedHandler = null; + if (isset($app['security.access_denied_handler.'.$name])) { + $accessDeniedHandler = $app['security.access_denied_handler.'.$name]; + } + $app['security.exception_listener.'.$name] = $app['security.exception_listener._proto']($entryPoint, $name, $accessDeniedHandler); } } - $configs[$name] = array($pattern, $listeners, $protected); + $configs[$name] = array( + 'pattern' => $pattern, + 'listeners' => $listeners, + 'protected' => $protected, + 'methods' => $methods, + 'hosts' => $hosts, + ); } $app['security.authentication_providers'] = array_map(function ($provider) use ($app) { @@ -246,13 +310,19 @@ class SecurityServiceProvider implements ServiceProviderInterface $map = new FirewallMap(); foreach ($configs as $name => $config) { + if (is_string($config['pattern'])) { + $requestMatcher = new RequestMatcher($config['pattern'], $config['hosts'], $config['methods']); + } else { + $requestMatcher = $config['pattern']; + } + $map->add( - is_string($config[0]) ? new RequestMatcher($config[0]) : $config[0], + $requestMatcher, array_map(function ($listenerId) use ($app, $name) { $listener = $app[$listenerId]; if (isset($app['security.remember_me.service.'.$name])) { - if ($listener instanceof AbstractAuthenticationListener) { + if ($listener instanceof AbstractAuthenticationListener || $listener instanceof GuardAuthenticationListener) { $listener->setRememberMeServices($app['security.remember_me.service.'.$name]); } if ($listener instanceof LogoutListener) { @@ -261,106 +331,115 @@ class SecurityServiceProvider implements ServiceProviderInterface } return $listener; - }, $config[1]), - $config[2] ? $app['security.exception_listener.'.$name] : null + }, $config['listeners']), + $config['protected'] ? $app['security.exception_listener.'.$name] : null ); } return $map; - }); + }; - $app['security.access_listener'] = $app->share(function ($app) { + $app['security.access_listener'] = function ($app) { return new AccessListener( - $app['security'], + $app['security.token_storage'], $app['security.access_manager'], $app['security.access_map'], $app['security.authentication_manager'], $app['logger'] ); - }); + }; - $app['security.access_map'] = $app->share(function ($app) { + $app['security.access_map'] = function ($app) { $map = new AccessMap(); foreach ($app['security.access_rules'] as $rule) { if (is_string($rule[0])) { $rule[0] = new RequestMatcher($rule[0]); + } elseif (is_array($rule[0])) { + $rule[0] += array( + 'path' => null, + 'host' => null, + 'methods' => null, + 'ips' => null, + 'attributes' => array(), + 'schemes' => null, + ); + $rule[0] = new RequestMatcher($rule[0]['path'], $rule[0]['host'], $rule[0]['methods'], $rule[0]['ips'], $rule[0]['attributes'], $rule[0]['schemes']); } - $map->add($rule[0], (array) $rule[1], isset($rule[2]) ? $rule[2] : null); } return $map; - }); + }; - $app['security.trust_resolver'] = $app->share(function ($app) { + $app['security.trust_resolver'] = function ($app) { return new AuthenticationTrustResolver('Symfony\Component\Security\Core\Authentication\Token\AnonymousToken', 'Symfony\Component\Security\Core\Authentication\Token\RememberMeToken'); - }); + }; - $app['security.session_strategy'] = $app->share(function ($app) { - return new SessionAuthenticationStrategy('migrate'); - }); + $app['security.session_strategy'] = function ($app) { + return new SessionAuthenticationStrategy(SessionAuthenticationStrategy::MIGRATE); + }; - $app['security.http_utils'] = $app->share(function ($app) { - return new HttpUtils(isset($app['url_generator']) ? $app['url_generator'] : null, $app['url_matcher']); - }); + $app['security.http_utils'] = function ($app) { + return new HttpUtils($app['url_generator'], $app['request_matcher']); + }; $app['security.last_error'] = $app->protect(function (Request $request) { - if ($request->attributes->has(SecurityContextInterface::AUTHENTICATION_ERROR)) { - return $request->attributes->get(SecurityContextInterface::AUTHENTICATION_ERROR)->getMessage(); + if ($request->attributes->has(Security::AUTHENTICATION_ERROR)) { + return $request->attributes->get(Security::AUTHENTICATION_ERROR)->getMessage(); } $session = $request->getSession(); - if ($session && $session->has(SecurityContextInterface::AUTHENTICATION_ERROR)) { - $error = $session->get(SecurityContextInterface::AUTHENTICATION_ERROR)->getMessage(); - $session->remove(SecurityContextInterface::AUTHENTICATION_ERROR); + if ($session && $session->has(Security::AUTHENTICATION_ERROR)) { + $message = $session->get(Security::AUTHENTICATION_ERROR)->getMessage(); + $session->remove(Security::AUTHENTICATION_ERROR); - return $error; + return $message; } }); // prototypes (used by the Firewall Map) $app['security.context_listener._proto'] = $app->protect(function ($providerKey, $userProviders) use ($app) { - return $app->share(function () use ($app, $userProviders, $providerKey) { + return function () use ($app, $userProviders, $providerKey) { return new ContextListener( - $app['security'], + $app['security.token_storage'], $userProviders, $providerKey, $app['logger'], $app['dispatcher'] ); - }); + }; }); $app['security.user_provider.inmemory._proto'] = $app->protect(function ($params) use ($app) { - return $app->share(function () use ($app, $params) { + return function () use ($app, $params) { $users = array(); foreach ($params as $name => $user) { $users[$name] = array('roles' => (array) $user[0], 'password' => $user[1]); } return new InMemoryUserProvider($users); - }); + }; }); - $app['security.exception_listener._proto'] = $app->protect(function ($entryPoint, $name) use ($app) { - return $app->share(function () use ($app, $entryPoint, $name) { + $app['security.exception_listener._proto'] = $app->protect(function ($entryPoint, $name, $accessDeniedHandler = null) use ($app) { + return function () use ($app, $entryPoint, $name, $accessDeniedHandler) { return new ExceptionListener( - $app['security'], + $app['security.token_storage'], $app['security.trust_resolver'], $app['security.http_utils'], $name, $app[$entryPoint], null, // errorPage - null, // AccessDeniedHandlerInterface + $accessDeniedHandler, $app['logger'] ); - }); + }; }); $app['security.authentication.success_handler._proto'] = $app->protect(function ($name, $options) use ($app) { - return $app->share(function () use ($name, $options, $app) { + return function () use ($name, $options, $app) { $handler = new DefaultAuthenticationSuccessHandler( $app['security.http_utils'], $options @@ -368,22 +447,43 @@ class SecurityServiceProvider implements ServiceProviderInterface $handler->setProviderKey($name); return $handler; - }); + }; }); $app['security.authentication.failure_handler._proto'] = $app->protect(function ($name, $options) use ($app) { - return $app->share(function () use ($name, $options, $app) { + return function () use ($name, $options, $app) { return new DefaultAuthenticationFailureHandler( $app, $app['security.http_utils'], $options, $app['logger'] ); - }); + }; + }); + + $app['security.authentication_listener.guard._proto'] = $app->protect(function ($providerKey, $options) use ($app, $that) { + return function () use ($app, $providerKey, $options, $that) { + if (!isset($app['security.authentication.guard_handler'])) { + $app['security.authentication.guard_handler'] = new GuardAuthenticatorHandler($app['security.token_storage'], $app['dispatcher']); + } + + $authenticators = array(); + foreach ($options['authenticators'] as $authenticatorId) { + $authenticators[] = $app[$authenticatorId]; + } + + return new GuardAuthenticationListener( + $app['security.authentication.guard_handler'], + $app['security.authentication_manager'], + $providerKey, + $authenticators, + $app['logger'] + ); + }; }); $app['security.authentication_listener.form._proto'] = $app->protect(function ($name, $options) use ($app, $that) { - return $app->share(function () use ($app, $name, $options, $that) { + return function () use ($app, $name, $options, $that) { $that->addFakeRoute( 'match', $tmp = isset($options['check_path']) ? $options['check_path'] : '/login_check', @@ -401,7 +501,7 @@ class SecurityServiceProvider implements ServiceProviderInterface } return new $class( - $app['security'], + $app['security.token_storage'], $app['security.authentication_manager'], isset($app['security.session_strategy.'.$name]) ? $app['security.session_strategy.'.$name] : $app['security.session_strategy'], $app['security.http_utils'], @@ -411,44 +511,44 @@ class SecurityServiceProvider implements ServiceProviderInterface $options, $app['logger'], $app['dispatcher'], - isset($options['with_csrf']) && $options['with_csrf'] && isset($app['form.csrf_provider']) ? $app['form.csrf_provider'] : null + isset($options['with_csrf']) && $options['with_csrf'] && isset($app['csrf.token_manager']) ? $app['csrf.token_manager'] : null ); - }); + }; }); $app['security.authentication_listener.http._proto'] = $app->protect(function ($providerKey, $options) use ($app) { - return $app->share(function () use ($app, $providerKey, $options) { + return function () use ($app, $providerKey, $options) { return new BasicAuthenticationListener( - $app['security'], + $app['security.token_storage'], $app['security.authentication_manager'], $providerKey, $app['security.entry_point.'.$providerKey.'.http'], $app['logger'] ); - }); + }; }); $app['security.authentication_listener.anonymous._proto'] = $app->protect(function ($providerKey, $options) use ($app) { - return $app->share(function () use ($app, $providerKey, $options) { + return function () use ($app, $providerKey, $options) { return new AnonymousAuthenticationListener( - $app['security'], + $app['security.token_storage'], $providerKey, $app['logger'] ); - }); + }; }); $app['security.authentication.logout_handler._proto'] = $app->protect(function ($name, $options) use ($app) { - return $app->share(function () use ($name, $options, $app) { + return function () use ($name, $options, $app) { return new DefaultLogoutSuccessHandler( $app['security.http_utils'], isset($options['target_url']) ? $options['target_url'] : '/' ); - }); + }; }); $app['security.authentication_listener.logout._proto'] = $app->protect(function ($name, $options) use ($app, $that) { - return $app->share(function () use ($app, $name, $options, $that) { + return function () use ($app, $name, $options, $that) { $that->addFakeRoute( 'get', $tmp = isset($options['logout_path']) ? $options['logout_path'] : '/logout', @@ -460,23 +560,26 @@ class SecurityServiceProvider implements ServiceProviderInterface } $listener = new LogoutListener( - $app['security'], + $app['security.token_storage'], $app['security.http_utils'], $app['security.authentication.logout_handler.'.$name], $options, - isset($options['with_csrf']) && $options['with_csrf'] && isset($app['form.csrf_provider']) ? $app['form.csrf_provider'] : null + isset($options['with_csrf']) && $options['with_csrf'] && isset($app['csrf.token_manager']) ? $app['csrf.token_manager'] : null ); - $listener->addHandler(new SessionLogoutHandler()); + $invalidateSession = isset($options['invalidate_session']) ? $options['invalidate_session'] : true; + if (true === $invalidateSession && false === $options['stateless']) { + $listener->addHandler(new SessionLogoutHandler()); + } return $listener; - }); + }; }); $app['security.authentication_listener.switch_user._proto'] = $app->protect(function ($name, $options) use ($app, $that) { - return $app->share(function () use ($app, $name, $options, $that) { + return function () use ($app, $name, $options, $that) { return new SwitchUserListener( - $app['security'], + $app['security.token_storage'], $app['security.user_provider.'.$name], $app['security.user_checker'], $name, @@ -486,26 +589,43 @@ class SecurityServiceProvider implements ServiceProviderInterface isset($options['role']) ? $options['role'] : 'ROLE_ALLOWED_TO_SWITCH', $app['dispatcher'] ); - }); + }; }); $app['security.entry_point.form._proto'] = $app->protect(function ($name, array $options) use ($app) { - return $app->share(function () use ($app, $options) { + return function () use ($app, $options) { $loginPath = isset($options['login_path']) ? $options['login_path'] : '/login'; $useForward = isset($options['use_forward']) ? $options['use_forward'] : false; return new FormAuthenticationEntryPoint($app, $app['security.http_utils'], $loginPath, $useForward); - }); + }; }); $app['security.entry_point.http._proto'] = $app->protect(function ($name, array $options) use ($app) { - return $app->share(function () use ($app, $name, $options) { + return function () use ($app, $name, $options) { return new BasicAuthenticationEntryPoint(isset($options['real_name']) ? $options['real_name'] : 'Secured'); - }); + }; }); - $app['security.authentication_provider.dao._proto'] = $app->protect(function ($name) use ($app) { - return $app->share(function () use ($app, $name) { + $app['security.entry_point.guard._proto'] = $app->protect(function ($name, array $options) use ($app) { + if (isset($options['entry_point'])) { + // if it's configured explicitly, use it! + return $app[$options['entry_point']]; + } + $authenticatorIds = $options['authenticators']; + if (count($authenticatorIds) == 1) { + // if there is only one authenticator, use that as the entry point + return $app[reset($authenticatorIds)]; + } + // we have multiple entry points - we must ask them to configure one + throw new \LogicException(sprintf( + 'Because you have multiple guard configurators, you need to set the "guard.entry_point" key to one of your configurators (%s)', + implode(', ', $authenticatorIds) + )); + }); + + $app['security.authentication_provider.dao._proto'] = $app->protect(function ($name, $options) use ($app) { + return function () use ($app, $name) { return new DaoAuthenticationProvider( $app['security.user_provider.'.$name], $app['security.user_checker'], @@ -513,37 +633,60 @@ class SecurityServiceProvider implements ServiceProviderInterface $app['security.encoder_factory'], $app['security.hide_user_not_found'] ); - }); + }; }); - $app['security.authentication_provider.anonymous._proto'] = $app->protect(function ($name) use ($app) { - return $app->share(function () use ($app, $name) { + $app['security.authentication_provider.guard._proto'] = $app->protect(function ($name, $options) use ($app) { + return function () use ($app, $name, $options) { + $authenticators = array(); + foreach ($options['authenticators'] as $authenticatorId) { + $authenticators[] = $app[$authenticatorId]; + } + + return new GuardAuthenticationProvider( + $authenticators, + $app['security.user_provider.'.$name], + $name, + $app['security.user_checker'] + ); + }; + }); + + $app['security.authentication_provider.anonymous._proto'] = $app->protect(function ($name, $options) use ($app) { + return function () use ($app, $name) { return new AnonymousAuthenticationProvider($name); - }); + }; }); if (isset($app['validator'])) { - $app['security.validator.user_password_validator'] = $app->share(function ($app) { - return new UserPasswordValidator($app['security'], $app['security.encoder_factory']); - }); - - if (!isset($app['validator.validator_service_ids'])) { - $app['validator.validator_service_ids'] = array(); - } + $app['security.validator.user_password_validator'] = function ($app) { + return new UserPasswordValidator($app['security.token_storage'], $app['security.encoder_factory']); + }; $app['validator.validator_service_ids'] = array_merge($app['validator.validator_service_ids'], array('security.validator.user_password' => 'security.validator.user_password_validator')); } } - public function boot(Application $app) + public function subscribe(Container $app, EventDispatcherInterface $dispatcher) { - $app['dispatcher']->addSubscriber($app['security.firewall']); + $dispatcher->addSubscriber($app['security.firewall']); + } + public function connect(Application $app) + { + $controllers = $app['controllers_factory']; foreach ($this->fakeRoutes as $route) { list($method, $pattern, $name) = $route; - $app->$method($pattern)->run(null)->bind($name); + $controllers->$method($pattern)->run(null)->bind($name); } + + return $controllers; + } + + public function boot(Application $app) + { + $app->mount('/', $this->connect($app)); } public function addFakeRoute($method, $pattern, $name) diff --git a/lib/silex/vendor/silex/silex/src/Silex/Provider/SerializerServiceProvider.php b/lib/silex/vendor/silex/silex/src/Silex/Provider/SerializerServiceProvider.php index 3ba24a810..8986abeff 100644 --- a/lib/silex/vendor/silex/silex/src/Silex/Provider/SerializerServiceProvider.php +++ b/lib/silex/vendor/silex/silex/src/Silex/Provider/SerializerServiceProvider.php @@ -11,8 +11,8 @@ namespace Silex\Provider; -use Silex\Application; -use Silex\ServiceProviderInterface; +use Pimple\Container; +use Pimple\ServiceProviderInterface; use Symfony\Component\Serializer\Serializer; use Symfony\Component\Serializer\Encoder\JsonEncoder; use Symfony\Component\Serializer\Encoder\XmlEncoder; @@ -32,32 +32,19 @@ class SerializerServiceProvider implements ServiceProviderInterface * * This method registers a serializer service. {@link http://api.symfony.com/master/Symfony/Component/Serializer/Serializer.html * The service is provided by the Symfony Serializer component}. - * - * @param Application $app */ - public function register(Application $app) + public function register(Container $app) { - $app['serializer'] = $app->share(function () use ($app) { + $app['serializer'] = function ($app) { return new Serializer($app['serializer.normalizers'], $app['serializer.encoders']); - }); + }; - $app['serializer.encoders'] = $app->share(function () { + $app['serializer.encoders'] = function () { return array(new JsonEncoder(), new XmlEncoder()); - }); + }; - $app['serializer.normalizers'] = $app->share(function () { + $app['serializer.normalizers'] = function () { return array(new CustomNormalizer(), new GetSetMethodNormalizer()); - }); - } - - /** - * {@inheritdoc} - * - * This provider does not execute any code when booting. - * - * @param Application $app - */ - public function boot(Application $app) - { + }; } } diff --git a/lib/silex/vendor/silex/silex/src/Silex/Provider/ServiceControllerServiceProvider.php b/lib/silex/vendor/silex/silex/src/Silex/Provider/ServiceControllerServiceProvider.php index a35a0344c..1c38adc9c 100644 --- a/lib/silex/vendor/silex/silex/src/Silex/Provider/ServiceControllerServiceProvider.php +++ b/lib/silex/vendor/silex/silex/src/Silex/Provider/ServiceControllerServiceProvider.php @@ -11,21 +11,16 @@ namespace Silex\Provider; -use Silex\Application; -use Silex\ServiceProviderInterface; +use Pimple\Container; +use Pimple\ServiceProviderInterface; use Silex\ServiceControllerResolver; class ServiceControllerServiceProvider implements ServiceProviderInterface { - public function register(Application $app) + public function register(Container $app) { - $app['resolver'] = $app->share($app->extend('resolver', function ($resolver, $app) { + $app->extend('resolver', function ($resolver, $app) { return new ServiceControllerResolver($resolver, $app['callback_resolver']); - })); - } - - public function boot(Application $app) - { - // noop + }); } } diff --git a/lib/silex/vendor/silex/silex/src/Silex/Provider/Session/SessionListener.php b/lib/silex/vendor/silex/silex/src/Silex/Provider/Session/SessionListener.php new file mode 100644 index 000000000..aba4c4e86 --- /dev/null +++ b/lib/silex/vendor/silex/silex/src/Silex/Provider/Session/SessionListener.php @@ -0,0 +1,39 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Silex\Provider\Session; + +use Pimple\Container; +use Symfony\Component\HttpKernel\EventListener\SessionListener as BaseSessionListener; + +/** + * Sets the session in the request. + * + * @author Fabien Potencier + */ +class SessionListener extends BaseSessionListener +{ + private $app; + + public function __construct(Container $app) + { + $this->app = $app; + } + + protected function getSession() + { + if (!isset($this->app['session'])) { + return; + } + + return $this->app['session']; + } +} diff --git a/lib/silex/vendor/silex/silex/src/Silex/Provider/Session/TestSessionListener.php b/lib/silex/vendor/silex/silex/src/Silex/Provider/Session/TestSessionListener.php new file mode 100644 index 000000000..ab98eb12b --- /dev/null +++ b/lib/silex/vendor/silex/silex/src/Silex/Provider/Session/TestSessionListener.php @@ -0,0 +1,39 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Silex\Provider\Session; + +use Pimple\Container; +use Symfony\Component\HttpKernel\EventListener\TestSessionListener as BaseTestSessionListener; + +/** + * Simulates sessions for testing purpose. + * + * @author Fabien Potencier + */ +class TestSessionListener extends BaseTestSessionListener +{ + private $app; + + public function __construct(Container $app) + { + $this->app = $app; + } + + protected function getSession() + { + if (!isset($this->app['session'])) { + return; + } + + return $this->app['session']; + } +} diff --git a/lib/silex/vendor/silex/silex/src/Silex/Provider/SessionServiceProvider.php b/lib/silex/vendor/silex/silex/src/Silex/Provider/SessionServiceProvider.php index dcd94c026..a51e230e3 100644 --- a/lib/silex/vendor/silex/silex/src/Silex/Provider/SessionServiceProvider.php +++ b/lib/silex/vendor/silex/silex/src/Silex/Provider/SessionServiceProvider.php @@ -11,114 +11,76 @@ namespace Silex\Provider; -use Silex\Application; -use Silex\ServiceProviderInterface; +use Pimple\Container; +use Pimple\ServiceProviderInterface; +use Silex\Api\EventListenerProviderInterface; +use Silex\Provider\Session\SessionListener; +use Silex\Provider\Session\TestSessionListener; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\HttpFoundation\Session\Storage\Handler\NativeFileSessionHandler; use Symfony\Component\HttpFoundation\Session\Storage\NativeSessionStorage; use Symfony\Component\HttpFoundation\Session\Storage\MockFileSessionStorage; use Symfony\Component\HttpFoundation\Session\Session; -use Symfony\Component\HttpFoundation\Cookie; -use Symfony\Component\HttpKernel\HttpKernelInterface; -use Symfony\Component\HttpKernel\KernelEvents; -use Symfony\Component\HttpKernel\Event\FilterResponseEvent; -use Symfony\Component\HttpKernel\Event\GetResponseEvent; /** * Symfony HttpFoundation component Provider for sessions. * * @author Fabien Potencier */ -class SessionServiceProvider implements ServiceProviderInterface +class SessionServiceProvider implements ServiceProviderInterface, EventListenerProviderInterface { - private $app; - - public function register(Application $app) + public function register(Container $app) { - $this->app = $app; - $app['session.test'] = false; - $app['session'] = $app->share(function ($app) { - if (!isset($app['session.storage'])) { - if ($app['session.test']) { - $app['session.storage'] = $app['session.storage.test']; - } else { - $app['session.storage'] = $app['session.storage.native']; - } + $app['session'] = function ($app) { + return new Session($app['session.storage'], $app['session.attribute_bag'], $app['session.flash_bag']); + }; + + $app['session.storage'] = function ($app) { + if ($app['session.test']) { + return $app['session.storage.test']; } - return new Session($app['session.storage']); - }); + return $app['session.storage.native']; + }; - $app['session.storage.handler'] = $app->share(function ($app) { + $app['session.storage.handler'] = function ($app) { return new NativeFileSessionHandler($app['session.storage.save_path']); - }); + }; - $app['session.storage.native'] = $app->share(function ($app) { + $app['session.storage.native'] = function ($app) { return new NativeSessionStorage( $app['session.storage.options'], $app['session.storage.handler'] ); - }); + }; - $app['session.storage.test'] = $app->share(function () { + $app['session.listener'] = function ($app) { + return new SessionListener($app); + }; + + $app['session.storage.test'] = function () { return new MockFileSessionStorage(); - }); + }; + + $app['session.listener.test'] = function ($app) { + return new TestSessionListener($app); + }; $app['session.storage.options'] = array(); $app['session.default_locale'] = 'en'; $app['session.storage.save_path'] = null; + $app['session.attribute_bag'] = null; + $app['session.flash_bag'] = null; } - public function onEarlyKernelRequest(GetResponseEvent $event) + public function subscribe(Container $app, EventDispatcherInterface $dispatcher) { - $event->getRequest()->setSession($this->app['session']); - } - - public function onKernelRequest(GetResponseEvent $event) - { - if (HttpKernelInterface::MASTER_REQUEST !== $event->getRequestType()) { - return; - } - - // bootstrap the session - if (!isset($this->app['session'])) { - return; - } - - $session = $this->app['session']; - $cookies = $event->getRequest()->cookies; - - if ($cookies->has($session->getName())) { - $session->setId($cookies->get($session->getName())); - } else { - $session->migrate(false); - } - } - - public function onKernelResponse(FilterResponseEvent $event) - { - if (HttpKernelInterface::MASTER_REQUEST !== $event->getRequestType()) { - return; - } - - $session = $event->getRequest()->getSession(); - if ($session && $session->isStarted()) { - $session->save(); - - $params = session_get_cookie_params(); - - $event->getResponse()->headers->setCookie(new Cookie($session->getName(), $session->getId(), 0 === $params['lifetime'] ? 0 : time() + $params['lifetime'], $params['path'], $params['domain'], $params['secure'], $params['httponly'])); - } - } - - public function boot(Application $app) - { - $app['dispatcher']->addListener(KernelEvents::REQUEST, array($this, 'onEarlyKernelRequest'), 128); + $dispatcher->addSubscriber($app['session.listener']); if ($app['session.test']) { - $app['dispatcher']->addListener(KernelEvents::REQUEST, array($this, 'onKernelRequest'), 192); - $app['dispatcher']->addListener(KernelEvents::RESPONSE, array($this, 'onKernelResponse'), -128); + $app['dispatcher']->addSubscriber($app['session.listener.test']); } } } diff --git a/lib/silex/vendor/silex/silex/src/Silex/Provider/SwiftmailerServiceProvider.php b/lib/silex/vendor/silex/silex/src/Silex/Provider/SwiftmailerServiceProvider.php index 42646c1aa..541993f6b 100644 --- a/lib/silex/vendor/silex/silex/src/Silex/Provider/SwiftmailerServiceProvider.php +++ b/lib/silex/vendor/silex/silex/src/Silex/Provider/SwiftmailerServiceProvider.php @@ -11,39 +11,44 @@ namespace Silex\Provider; -use Silex\Application; -use Silex\ServiceProviderInterface; +use Pimple\Container; +use Pimple\ServiceProviderInterface; +use Silex\Api\EventListenerProviderInterface; +use Symfony\Component\Console\ConsoleEvents; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; +use Symfony\Component\HttpKernel\KernelEvents; +use Symfony\Component\HttpKernel\Event\PostResponseEvent; /** * Swiftmailer Provider. * * @author Fabien Potencier */ -class SwiftmailerServiceProvider implements ServiceProviderInterface +class SwiftmailerServiceProvider implements ServiceProviderInterface, EventListenerProviderInterface { - public function register(Application $app) + public function register(Container $app) { $app['swiftmailer.options'] = array(); $app['swiftmailer.use_spool'] = true; $app['mailer.initialized'] = false; - $app['mailer'] = $app->share(function ($app) { + $app['mailer'] = function ($app) { $app['mailer.initialized'] = true; $transport = $app['swiftmailer.use_spool'] ? $app['swiftmailer.spooltransport'] : $app['swiftmailer.transport']; return new \Swift_Mailer($transport); - }); + }; - $app['swiftmailer.spooltransport'] = $app->share(function ($app) { + $app['swiftmailer.spooltransport'] = function ($app) { return new \Swift_Transport_SpoolTransport($app['swiftmailer.transport.eventdispatcher'], $app['swiftmailer.spool']); - }); + }; - $app['swiftmailer.spool'] = $app->share(function ($app) { + $app['swiftmailer.spool'] = function ($app) { return new \Swift_MemorySpool(); - }); + }; - $app['swiftmailer.transport'] = $app->share(function ($app) { + $app['swiftmailer.transport'] = function ($app) { $transport = new \Swift_Transport_EsmtpTransport( $app['swiftmailer.transport.buffer'], array($app['swiftmailer.transport.authhandler']), @@ -51,12 +56,13 @@ class SwiftmailerServiceProvider implements ServiceProviderInterface ); $options = $app['swiftmailer.options'] = array_replace(array( - 'host' => 'localhost', - 'port' => 25, - 'username' => '', - 'password' => '', + 'host' => 'localhost', + 'port' => 25, + 'username' => '', + 'password' => '', 'encryption' => null, - 'auth_mode' => null, + 'auth_mode' => null, + 'stream_context_options' => [], ), $app['swiftmailer.options']); $transport->setHost($options['host']); @@ -65,35 +71,70 @@ class SwiftmailerServiceProvider implements ServiceProviderInterface $transport->setUsername($options['username']); $transport->setPassword($options['password']); $transport->setAuthMode($options['auth_mode']); + $transport->setStreamOptions($options['stream_context_options']); return $transport; - }); + }; - $app['swiftmailer.transport.buffer'] = $app->share(function () { + $app['swiftmailer.transport.buffer'] = function () { return new \Swift_Transport_StreamBuffer(new \Swift_StreamFilters_StringReplacementFilterFactory()); - }); + }; - $app['swiftmailer.transport.authhandler'] = $app->share(function () { + $app['swiftmailer.transport.authhandler'] = function () { return new \Swift_Transport_Esmtp_AuthHandler(array( new \Swift_Transport_Esmtp_Auth_CramMd5Authenticator(), new \Swift_Transport_Esmtp_Auth_LoginAuthenticator(), new \Swift_Transport_Esmtp_Auth_PlainAuthenticator(), )); - }); + }; - $app['swiftmailer.transport.eventdispatcher'] = $app->share(function () { - return new \Swift_Events_SimpleEventDispatcher(); - }); + $app['swiftmailer.transport.eventdispatcher'] = function ($app) { + $dispatcher = new \Swift_Events_SimpleEventDispatcher(); + + $plugins = $app['swiftmailer.plugins']; + + if (null !== $app['swiftmailer.sender_address']) { + $plugins[] = new \Swift_Plugins_ImpersonatePlugin($app['swiftmailer.sender_address']); + } + + if (!empty($app['swiftmailer.delivery_addresses'])) { + $plugins[] = new \Swift_Plugins_RedirectingPlugin( + $app['swiftmailer.delivery_addresses'], + $app['swiftmailer.delivery_whitelist'] + ); + } + + foreach ($plugins as $plugin) { + $dispatcher->bindEventListener($plugin); + } + + return $dispatcher; + }; + + $app['swiftmailer.plugins'] = function ($app) { + return array(); + }; + + $app['swiftmailer.sender_address'] = null; + $app['swiftmailer.delivery_addresses'] = array(); + $app['swiftmailer.delivery_whitelist'] = array(); } - public function boot(Application $app) + public function subscribe(Container $app, EventDispatcherInterface $dispatcher) { - $app->finish(function () use ($app) { + // Event has no typehint as it can be either a PostResponseEvent or a ConsoleTerminateEvent + $onTerminate = function ($event) use ($app) { // To speed things up (by avoiding Swift Mailer initialization), flush // messages only if our mailer has been created (potentially used) - if ($app['mailer.initialized']) { + if ($app['mailer.initialized'] && $app['swiftmailer.use_spool'] && $app['swiftmailer.spooltransport'] instanceof \Swift_Transport_SpoolTransport) { $app['swiftmailer.spooltransport']->getSpool()->flushQueue($app['swiftmailer.transport']); } - }); + }; + + $dispatcher->addListener(KernelEvents::TERMINATE, $onTerminate); + + if (class_exists('Symfony\Component\Console\ConsoleEvents')) { + $dispatcher->addListener(ConsoleEvents::TERMINATE, $onTerminate); + } } } diff --git a/lib/silex/vendor/silex/silex/src/Silex/Provider/TranslationServiceProvider.php b/lib/silex/vendor/silex/silex/src/Silex/Provider/TranslationServiceProvider.php index 730e82370..a9ee55c99 100644 --- a/lib/silex/vendor/silex/silex/src/Silex/Provider/TranslationServiceProvider.php +++ b/lib/silex/vendor/silex/silex/src/Silex/Provider/TranslationServiceProvider.php @@ -11,35 +11,56 @@ namespace Silex\Provider; -use Silex\Application; -use Silex\ServiceProviderInterface; -use Silex\Translator; +use Pimple\Container; +use Pimple\ServiceProviderInterface; +use Symfony\Component\Translation\Translator; use Symfony\Component\Translation\MessageSelector; use Symfony\Component\Translation\Loader\ArrayLoader; use Symfony\Component\Translation\Loader\XliffFileLoader; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; +use Symfony\Component\HttpKernel\EventListener\TranslatorListener; +use Silex\Api\EventListenerProviderInterface; /** * Symfony Translation component Provider. * * @author Fabien Potencier */ -class TranslationServiceProvider implements ServiceProviderInterface +class TranslationServiceProvider implements ServiceProviderInterface, EventListenerProviderInterface { - public function register(Application $app) + public function register(Container $app) { - $app['translator'] = $app->share(function ($app) { - $translator = new Translator($app, $app['translator.message_selector']); - - // Handle deprecated 'locale_fallback' - if (isset($app['locale_fallback'])) { - $app['locale_fallbacks'] = (array) $app['locale_fallback']; + $app['translator'] = function ($app) { + if (!isset($app['locale'])) { + throw new \LogicException('You must define \'locale\' parameter or register the LocaleServiceProvider to use the TranslationServiceProvider'); } + $translator = new Translator($app['locale'], $app['translator.message_selector'], $app['translator.cache_dir'], $app['debug']); $translator->setFallbackLocales($app['locale_fallbacks']); - $translator->addLoader('array', new ArrayLoader()); $translator->addLoader('xliff', new XliffFileLoader()); + if (isset($app['validator'])) { + $r = new \ReflectionClass('Symfony\Component\Validator\Validation'); + $file = dirname($r->getFilename()).'/Resources/translations/validators.'.$app['locale'].'.xlf'; + if (file_exists($file)) { + $translator->addResource('xliff', $file, $app['locale'], 'validators'); + } + } + + if (isset($app['form.factory'])) { + $r = new \ReflectionClass('Symfony\Component\Form\Form'); + $file = dirname($r->getFilename()).'/Resources/translations/validators.'.$app['locale'].'.xlf'; + if (file_exists($file)) { + $translator->addResource('xliff', $file, $app['locale'], 'validators'); + } + } + + // Register default resources + foreach ($app['translator.resources'] as $resource) { + $translator->addResource($resource[0], $resource[1], $resource[2], $resource[3]); + } + foreach ($app['translator.domains'] as $domain => $data) { foreach ($data as $locale => $messages) { $translator->addResource('array', $messages, $locale, $domain); @@ -47,17 +68,31 @@ class TranslationServiceProvider implements ServiceProviderInterface } return $translator; - }); + }; - $app['translator.message_selector'] = $app->share(function () { + if (isset($app['request_stack'])) { + $app['translator.listener'] = function ($app) { + return new TranslatorListener($app['translator'], $app['request_stack']); + }; + } + + $app['translator.message_selector'] = function () { return new MessageSelector(); - }); + }; + + $app['translator.resources'] = function ($app) { + return array(); + }; $app['translator.domains'] = array(); $app['locale_fallbacks'] = array('en'); + $app['translator.cache_dir'] = null; } - public function boot(Application $app) + public function subscribe(Container $app, EventDispatcherInterface $dispatcher) { + if (isset($app['translator.listener'])) { + $dispatcher->addSubscriber($app['translator.listener']); + } } } diff --git a/lib/silex/vendor/silex/silex/src/Silex/Provider/Twig/RuntimeLoader.php b/lib/silex/vendor/silex/silex/src/Silex/Provider/Twig/RuntimeLoader.php new file mode 100644 index 000000000..9a7aa915e --- /dev/null +++ b/lib/silex/vendor/silex/silex/src/Silex/Provider/Twig/RuntimeLoader.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 Silex\Provider\Twig; + +use Pimple\Container; + +/** + * Loads Twig extension runtimes via Pimple. + * + * @author Fabien Potencier + */ +class RuntimeLoader implements \Twig_RuntimeLoaderInterface +{ + private $container; + private $mapping; + + public function __construct(Container $container, array $mapping) + { + $this->container = $container; + $this->mapping = $mapping; + } + + /** + * {@inheritdoc} + */ + public function load($class) + { + if (isset($this->mapping[$class])) { + return $this->container[$this->mapping[$class]]; + } + } +} diff --git a/lib/silex/vendor/silex/silex/src/Silex/Provider/TwigServiceProvider.php b/lib/silex/vendor/silex/silex/src/Silex/Provider/TwigServiceProvider.php index 1a4351927..a5c43ccbc 100644 --- a/lib/silex/vendor/silex/silex/src/Silex/Provider/TwigServiceProvider.php +++ b/lib/silex/vendor/silex/silex/src/Silex/Provider/TwigServiceProvider.php @@ -11,15 +11,23 @@ namespace Silex\Provider; -use Silex\Application; -use Silex\ServiceProviderInterface; +use Pimple\Container; +use Pimple\ServiceProviderInterface; +use Silex\Provider\Twig\RuntimeLoader; +use Symfony\Bridge\Twig\AppVariable; +use Symfony\Bridge\Twig\Extension\AssetExtension; +use Symfony\Bridge\Twig\Extension\DumpExtension; use Symfony\Bridge\Twig\Extension\RoutingExtension; use Symfony\Bridge\Twig\Extension\TranslationExtension; use Symfony\Bridge\Twig\Extension\FormExtension; use Symfony\Bridge\Twig\Extension\SecurityExtension; +use Symfony\Bridge\Twig\Extension\HttpFoundationExtension; use Symfony\Bridge\Twig\Extension\HttpKernelExtension; +use Symfony\Bridge\Twig\Extension\WebLinkExtension; use Symfony\Bridge\Twig\Form\TwigRendererEngine; use Symfony\Bridge\Twig\Form\TwigRenderer; +use Symfony\Bridge\Twig\Extension\HttpKernelRuntime; +use Symfony\Component\WebLink\HttpHeaderSerializer; /** * Twig integration for Silex. @@ -28,31 +36,67 @@ use Symfony\Bridge\Twig\Form\TwigRenderer; */ class TwigServiceProvider implements ServiceProviderInterface { - public function register(Application $app) + public function register(Container $app) { $app['twig.options'] = array(); $app['twig.form.templates'] = array('form_div_layout.html.twig'); $app['twig.path'] = array(); $app['twig.templates'] = array(); - $app['twig'] = $app->share(function ($app) { + $app['twig.date.format'] = 'F j, Y H:i'; + $app['twig.date.interval_format'] = '%d days'; + $app['twig.date.timezone'] = null; + + $app['twig.number_format.decimals'] = 0; + $app['twig.number_format.decimal_point'] = '.'; + $app['twig.number_format.thousands_separator'] = ','; + + $app['twig'] = function ($app) { $app['twig.options'] = array_replace( array( - 'charset' => $app['charset'], - 'debug' => $app['debug'], + 'charset' => $app['charset'], + 'debug' => $app['debug'], 'strict_variables' => $app['debug'], ), $app['twig.options'] ); - $twig = new \Twig_Environment($app['twig.loader'], $app['twig.options']); + $twig = $app['twig.environment_factory']($app); + // registered for BC, but should not be used anymore + // deprecated and should probably be removed in Silex 3.0 $twig->addGlobal('app', $app); + $coreExtension = $twig->getExtension('Twig_Extension_Core'); + + $coreExtension->setDateFormat($app['twig.date.format'], $app['twig.date.interval_format']); + + if (null !== $app['twig.date.timezone']) { + $coreExtension->setTimezone($app['twig.date.timezone']); + } + + $coreExtension->setNumberFormat($app['twig.number_format.decimals'], $app['twig.number_format.decimal_point'], $app['twig.number_format.thousands_separator']); + if ($app['debug']) { $twig->addExtension(new \Twig_Extension_Debug()); } if (class_exists('Symfony\Bridge\Twig\Extension\RoutingExtension')) { - if (isset($app['url_generator'])) { + $app['twig.app_variable'] = function ($app) { + $var = new AppVariable(); + if (isset($app['security.token_storage'])) { + $var->setTokenStorage($app['security.token_storage']); + } + if (isset($app['request_stack'])) { + $var->setRequestStack($app['request_stack']); + } + $var->setDebug($app['debug']); + + return $var; + }; + + $twig->addGlobal('global', $app['twig.app_variable']); + + if (isset($app['request_stack'])) { + $twig->addExtension(new HttpFoundationExtension($app['request_stack'])); $twig->addExtension(new RoutingExtension($app['url_generator'])); } @@ -60,57 +104,96 @@ class TwigServiceProvider implements ServiceProviderInterface $twig->addExtension(new TranslationExtension($app['translator'])); } - if (isset($app['security'])) { - $twig->addExtension(new SecurityExtension($app['security'])); + if (isset($app['security.authorization_checker'])) { + $twig->addExtension(new SecurityExtension($app['security.authorization_checker'])); } if (isset($app['fragment.handler'])) { $app['fragment.renderer.hinclude']->setTemplating($twig); $twig->addExtension(new HttpKernelExtension($app['fragment.handler'])); - } else { - // fallback for BC, to be removed in 1.3 - $twig->addExtension(new TwigCoreExtension()); + } + + if (isset($app['assets.packages'])) { + $twig->addExtension(new AssetExtension($app['assets.packages'])); } if (isset($app['form.factory'])) { - $app['twig.form.engine'] = $app->share(function ($app) { - return new TwigRendererEngine($app['twig.form.templates']); - }); + $app['twig.form.engine'] = function ($app) use ($twig) { + return new TwigRendererEngine($app['twig.form.templates'], $twig); + }; - $app['twig.form.renderer'] = $app->share(function ($app) { - return new TwigRenderer($app['twig.form.engine'], $app['form.csrf_provider']); - }); + $app['twig.form.renderer'] = function ($app) { + $csrfTokenManager = isset($app['csrf.token_manager']) ? $app['csrf.token_manager'] : null; - $twig->addExtension(new FormExtension($app['twig.form.renderer'])); + return new TwigRenderer($app['twig.form.engine'], $csrfTokenManager); + }; + + $twig->addExtension(new FormExtension(class_exists(HttpKernelRuntime::class) ? null : $app['twig.form.renderer'])); // add loader for Symfony built-in form templates $reflected = new \ReflectionClass('Symfony\Bridge\Twig\Extension\FormExtension'); $path = dirname($reflected->getFileName()).'/../Resources/views/Form'; $app['twig.loader']->addLoader(new \Twig_Loader_Filesystem($path)); } + + if (isset($app['var_dumper.cloner'])) { + $twig->addExtension(new DumpExtension($app['var_dumper.cloner'])); + } + + if (class_exists(HttpKernelRuntime::class)) { + $twig->addRuntimeLoader($app['twig.runtime_loader']); + } + + if (class_exists(HttpHeaderSerializer::class) && class_exists(WebLinkExtension::class)) { + $twig->addExtension(new WebLinkExtension($app['request_stack'])); + } } return $twig; - }); + }; - $app['twig.loader.filesystem'] = $app->share(function ($app) { - return new \Twig_Loader_Filesystem($app['twig.path']); - }); + $app['twig.loader.filesystem'] = function ($app) { + $loader = new \Twig_Loader_Filesystem(); + foreach (is_array($app['twig.path']) ? $app['twig.path'] : array($app['twig.path']) as $key => $val) { + if (is_string($key)) { + $loader->addPath($key, $val); + } else { + $loader->addPath($val); + } + } - $app['twig.loader.array'] = $app->share(function ($app) { + return $loader; + }; + + $app['twig.loader.array'] = function ($app) { return new \Twig_Loader_Array($app['twig.templates']); - }); + }; - $app['twig.loader'] = $app->share(function ($app) { + $app['twig.loader'] = function ($app) { return new \Twig_Loader_Chain(array( $app['twig.loader.array'], $app['twig.loader.filesystem'], )); - }); - } + }; - public function boot(Application $app) - { + $app['twig.environment_factory'] = $app->protect(function ($app) { + return new \Twig_Environment($app['twig.loader'], $app['twig.options']); + }); + + $app['twig.runtime.httpkernel'] = function ($app) { + return new HttpKernelRuntime($app['fragment.handler']); + }; + + $app['twig.runtimes'] = function ($app) { + return array( + HttpKernelRuntime::class => 'twig.runtime.httpkernel', + TwigRenderer::class => 'twig.form.renderer', + ); + }; + + $app['twig.runtime_loader'] = function ($app) { + return new RuntimeLoader($app, $app['twig.runtimes']); + }; } } diff --git a/lib/silex/vendor/silex/silex/src/Silex/Provider/Validator/ConstraintValidatorFactory.php b/lib/silex/vendor/silex/silex/src/Silex/Provider/Validator/ConstraintValidatorFactory.php new file mode 100644 index 000000000..9f5e499d9 --- /dev/null +++ b/lib/silex/vendor/silex/silex/src/Silex/Provider/Validator/ConstraintValidatorFactory.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 Silex\Provider\Validator; + +use Pimple\Container; +use Symfony\Component\Validator\Constraint; +use Symfony\Component\Validator\ConstraintValidatorFactory as BaseConstraintValidatorFactory; + +/** + * Uses a service container to create constraint validators with dependencies. + * + * @author Kris Wallsmith + * @author Alex Kalyvitis + */ +class ConstraintValidatorFactory extends BaseConstraintValidatorFactory +{ + /** + * @var Container + */ + protected $container; + + /** + * @var array + */ + protected $serviceNames; + + /** + * Constructor. + * + * @param Container $container DI container + * @param array $serviceNames Validator service names + */ + public function __construct(Container $container, array $serviceNames = array(), $propertyAccessor = null) + { + parent::__construct($propertyAccessor); + + $this->container = $container; + $this->serviceNames = $serviceNames; + } + + /** + * {@inheritdoc} + */ + public function getInstance(Constraint $constraint) + { + $name = $constraint->validatedBy(); + + if (isset($this->serviceNames[$name])) { + return $this->container[$this->serviceNames[$name]]; + } + + return parent::getInstance($constraint); + } +} diff --git a/lib/silex/vendor/silex/silex/src/Silex/Provider/ValidatorServiceProvider.php b/lib/silex/vendor/silex/silex/src/Silex/Provider/ValidatorServiceProvider.php index 7c4323ba7..d89a3cb52 100644 --- a/lib/silex/vendor/silex/silex/src/Silex/Provider/ValidatorServiceProvider.php +++ b/lib/silex/vendor/silex/silex/src/Silex/Provider/ValidatorServiceProvider.php @@ -11,13 +11,13 @@ namespace Silex\Provider; -use Silex\Application; -use Silex\ServiceProviderInterface; -use Silex\ConstraintValidatorFactory; +use Pimple\Container; +use Pimple\ServiceProviderInterface; +use Silex\Provider\Validator\ConstraintValidatorFactory; use Symfony\Component\Validator\Validator; -use Symfony\Component\Validator\DefaultTranslator; -use Symfony\Component\Validator\Mapping\ClassMetadataFactory; +use Symfony\Component\Validator\Mapping\Factory\LazyLoadingMetadataFactory; use Symfony\Component\Validator\Mapping\Loader\StaticMethodLoader; +use Symfony\Component\Validator\Validation; /** * Symfony Validator component Provider. @@ -26,42 +26,37 @@ use Symfony\Component\Validator\Mapping\Loader\StaticMethodLoader; */ class ValidatorServiceProvider implements ServiceProviderInterface { - public function register(Application $app) + public function register(Container $app) { - $app['validator'] = $app->share(function ($app) { + $app['validator'] = function ($app) { + return $app['validator.builder']->getValidator(); + }; + + $app['validator.builder'] = function ($app) { + $builder = Validation::createValidatorBuilder(); + $builder->setConstraintValidatorFactory($app['validator.validator_factory']); + $builder->setTranslationDomain('validators'); + $builder->addObjectInitializers($app['validator.object_initializers']); + $builder->setMetadataFactory($app['validator.mapping.class_metadata_factory']); if (isset($app['translator'])) { - $r = new \ReflectionClass('Symfony\Component\Validator\Validator'); - $file = dirname($r->getFilename()).'/Resources/translations/validators.'.$app['locale'].'.xlf'; - if (file_exists($file)) { - $app['translator']->addResource('xliff', $file, $app['locale'], 'validators'); - } + $builder->setTranslator($app['translator']); } - return new Validator( - $app['validator.mapping.class_metadata_factory'], - $app['validator.validator_factory'], - isset($app['translator']) ? $app['translator'] : new DefaultTranslator(), - 'validators', - $app['validator.object_initializers'] - ); - }); + return $builder; + }; - $app['validator.mapping.class_metadata_factory'] = $app->share(function ($app) { - return new ClassMetadataFactory(new StaticMethodLoader()); - }); + $app['validator.mapping.class_metadata_factory'] = function ($app) { + return new LazyLoadingMetadataFactory(new StaticMethodLoader()); + }; - $app['validator.validator_factory'] = $app->share(function () use ($app) { - $validators = isset($app['validator.validator_service_ids']) ? $app['validator.validator_service_ids'] : array(); + $app['validator.validator_factory'] = function () use ($app) { + return new ConstraintValidatorFactory($app, $app['validator.validator_service_ids']); + }; - return new ConstraintValidatorFactory($app, $validators); - }); - - $app['validator.object_initializers'] = $app->share(function ($app) { + $app['validator.object_initializers'] = function ($app) { return array(); - }); - } + }; - public function boot(Application $app) - { + $app['validator.validator_service_ids'] = array(); } } diff --git a/lib/silex/vendor/silex/silex/src/Silex/Provider/VarDumperServiceProvider.php b/lib/silex/vendor/silex/silex/src/Silex/Provider/VarDumperServiceProvider.php new file mode 100644 index 000000000..7c40b5ee3 --- /dev/null +++ b/lib/silex/vendor/silex/silex/src/Silex/Provider/VarDumperServiceProvider.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 Silex\Provider; + +use Pimple\Container; +use Pimple\ServiceProviderInterface; +use Silex\Application; +use Silex\Api\BootableProviderInterface; +use Symfony\Component\VarDumper\VarDumper; +use Symfony\Component\VarDumper\Cloner\VarCloner; +use Symfony\Component\VarDumper\Dumper\CliDumper; + +/** + * Symfony Var Dumper component Provider. + * + * @author Fabien Potencier + */ +class VarDumperServiceProvider implements ServiceProviderInterface, BootableProviderInterface +{ + public function register(Container $app) + { + $app['var_dumper.cli_dumper'] = function ($app) { + return new CliDumper($app['var_dumper.dump_destination'], $app['charset']); + }; + + $app['var_dumper.cloner'] = function ($app) { + return new VarCloner(); + }; + + $app['var_dumper.dump_destination'] = null; + } + + public function boot(Application $app) + { + if (!$app['debug']) { + return; + } + + // This code is here to lazy load the dump stack. This default + // configuration for CLI mode is overridden in HTTP mode on + // 'kernel.request' event + VarDumper::setHandler(function ($var) use ($app) { + VarDumper::setHandler($handler = function ($var) use ($app) { + $app['var_dumper.cli_dumper']->dump($app['var_dumper.cloner']->cloneVar($var)); + }); + $handler($var); + }); + } +} diff --git a/lib/silex/vendor/silex/silex/src/Silex/Provider/composer.json b/lib/silex/vendor/silex/silex/src/Silex/Provider/composer.json new file mode 100644 index 000000000..a1e9fbfe0 --- /dev/null +++ b/lib/silex/vendor/silex/silex/src/Silex/Provider/composer.json @@ -0,0 +1,31 @@ +{ + "minimum-stability": "dev", + "name": "silex/providers", + "description": "The Silex providers", + "keywords": ["microframework"], + "homepage": "http://silex.sensiolabs.org", + "license": "MIT", + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Igor Wiedler", + "email": "igor@wiedler.ch" + } + ], + "require": { + "php": ">=5.5.9", + "pimple/pimple": "~3.0", + "silex/api": "~2.2" + }, + "autoload": { + "psr-4": { "Silex\\Provider\\": "" } + }, + "extra": { + "branch-alias": { + "dev-master": "2.2.x-dev" + } + } +} diff --git a/lib/silex/vendor/silex/silex/src/Silex/Route.php b/lib/silex/vendor/silex/silex/src/Silex/Route.php index 1ade9b784..99e82d87b 100644 --- a/lib/silex/vendor/silex/silex/src/Silex/Route.php +++ b/lib/silex/vendor/silex/silex/src/Silex/Route.php @@ -185,4 +185,18 @@ class Route extends BaseRoute return $this; } + + /** + * Sets a condition for the route to match. + * + * @param string $condition The condition + * + * @return Route $this The current Route instance + */ + public function when($condition) + { + $this->setCondition($condition); + + return $this; + } } diff --git a/lib/silex/vendor/silex/silex/src/Silex/Route/SecurityTrait.php b/lib/silex/vendor/silex/silex/src/Silex/Route/SecurityTrait.php index e29ec0a14..d42ba2fbb 100644 --- a/lib/silex/vendor/silex/silex/src/Silex/Route/SecurityTrait.php +++ b/lib/silex/vendor/silex/silex/src/Silex/Route/SecurityTrait.php @@ -23,7 +23,7 @@ trait SecurityTrait public function secure($roles) { $this->before(function ($request, $app) use ($roles) { - if (!$app['security']->isGranted($roles)) { + if (!$app['security.authorization_checker']->isGranted($roles)) { throw new AccessDeniedException(); } }); diff --git a/lib/silex/vendor/silex/silex/src/Silex/ViewListenerWrapper.php b/lib/silex/vendor/silex/silex/src/Silex/ViewListenerWrapper.php new file mode 100644 index 000000000..a67ec938e --- /dev/null +++ b/lib/silex/vendor/silex/silex/src/Silex/ViewListenerWrapper.php @@ -0,0 +1,87 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Silex; + +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\Event\GetResponseForControllerResultEvent; + +/** + * Wraps view listeners. + * + * @author Dave Marshall + */ +class ViewListenerWrapper +{ + private $app; + private $callback; + + /** + * Constructor. + * + * @param Application $app An Application instance + * @param mixed $callback + */ + public function __construct(Application $app, $callback) + { + $this->app = $app; + $this->callback = $callback; + } + + public function __invoke(GetResponseForControllerResultEvent $event) + { + $controllerResult = $event->getControllerResult(); + $callback = $this->app['callback_resolver']->resolveCallback($this->callback); + + if (!$this->shouldRun($callback, $controllerResult)) { + return; + } + + $response = call_user_func($callback, $controllerResult, $event->getRequest()); + + if ($response instanceof Response) { + $event->setResponse($response); + } elseif (null !== $response) { + $event->setControllerResult($response); + } + } + + private function shouldRun($callback, $controllerResult) + { + if (is_array($callback)) { + $callbackReflection = new \ReflectionMethod($callback[0], $callback[1]); + } elseif (is_object($callback) && !$callback instanceof \Closure) { + $callbackReflection = new \ReflectionObject($callback); + $callbackReflection = $callbackReflection->getMethod('__invoke'); + } else { + $callbackReflection = new \ReflectionFunction($callback); + } + + if ($callbackReflection->getNumberOfParameters() > 0) { + $parameters = $callbackReflection->getParameters(); + $expectedControllerResult = $parameters[0]; + + if ($expectedControllerResult->getClass() && (!is_object($controllerResult) || !$expectedControllerResult->getClass()->isInstance($controllerResult))) { + return false; + } + + if ($expectedControllerResult->isArray() && !is_array($controllerResult)) { + return false; + } + + if (method_exists($expectedControllerResult, 'isCallable') && $expectedControllerResult->isCallable() && !is_callable($controllerResult)) { + return false; + } + } + + return true; + } +} diff --git a/lib/silex/vendor/silex/silex/src/Silex/WebTestCase.php b/lib/silex/vendor/silex/silex/src/Silex/WebTestCase.php index 4adeaf33d..e72403a1c 100644 --- a/lib/silex/vendor/silex/silex/src/Silex/WebTestCase.php +++ b/lib/silex/vendor/silex/silex/src/Silex/WebTestCase.php @@ -11,20 +11,21 @@ namespace Silex; +use PHPUnit\Framework\TestCase; use Symfony\Component\HttpKernel\Client; -use Symfony\Component\HttpKernel\HttpKernel; +use Symfony\Component\HttpKernel\HttpKernelInterface; /** * WebTestCase is the base class for functional tests. * * @author Igor Wiedler */ -abstract class WebTestCase extends \PHPUnit_Framework_TestCase +abstract class WebTestCase extends TestCase { /** - * Application instance. + * HttpKernelInterface instance. * - * @var Application + * @var HttpKernelInterface */ protected $app; @@ -34,7 +35,7 @@ abstract class WebTestCase extends \PHPUnit_Framework_TestCase * Note: Child classes that define a setUp method must call * parent::setUp(). */ - public function setUp() + protected function setUp() { $this->app = $this->createApplication(); } @@ -42,19 +43,23 @@ abstract class WebTestCase extends \PHPUnit_Framework_TestCase /** * Creates the application. * - * @return HttpKernel + * @return HttpKernelInterface */ abstract public function createApplication(); /** * Creates a Client. * - * @param array $server An array of server parameters + * @param array $server Server parameters * * @return Client A Client instance */ public function createClient(array $server = array()) { + if (!class_exists('Symfony\Component\BrowserKit\Client')) { + throw new \LogicException('Component "symfony/browser-kit" is required by WebTestCase.'.PHP_EOL.'Run composer require symfony/browser-kit'); + } + return new Client($this->app, $server); } } diff --git a/lib/silex/vendor/silex/silex/tests/Silex/Tests/Application/FormTraitTest.php b/lib/silex/vendor/silex/silex/tests/Silex/Tests/Application/FormTraitTest.php index a0f68b463..1d36a0cb3 100644 --- a/lib/silex/vendor/silex/silex/tests/Silex/Tests/Application/FormTraitTest.php +++ b/lib/silex/vendor/silex/silex/tests/Silex/Tests/Application/FormTraitTest.php @@ -11,20 +11,28 @@ namespace Silex\Tests\Application; +use PHPUnit\Framework\TestCase; use Silex\Provider\FormServiceProvider; +use Symfony\Component\Form\FormBuilder; /** * FormTrait test cases. * * @author Fabien Potencier - * - * @requires PHP 5.4 */ -class FormTraitTest extends \PHPUnit_Framework_TestCase +class FormTraitTest extends TestCase { public function testForm() { - $this->assertInstanceOf('Symfony\Component\Form\FormBuilder', $this->createApplication()->form()); + $this->assertInstanceOf(FormBuilder::class, $this->createApplication()->form()); + } + + public function testNamedForm() + { + $builder = $this->createApplication()->namedForm('foo'); + + $this->assertInstanceOf(FormBuilder::class, $builder); + $this->assertSame('foo', $builder->getName()); } public function createApplication() diff --git a/lib/silex/vendor/silex/silex/tests/Silex/Tests/Application/MonologTraitTest.php b/lib/silex/vendor/silex/silex/tests/Silex/Tests/Application/MonologTraitTest.php index 9d46cc524..73dcda65b 100644 --- a/lib/silex/vendor/silex/silex/tests/Silex/Tests/Application/MonologTraitTest.php +++ b/lib/silex/vendor/silex/silex/tests/Silex/Tests/Application/MonologTraitTest.php @@ -11,6 +11,7 @@ namespace Silex\Tests\Application; +use PHPUnit\Framework\TestCase; use Silex\Provider\MonologServiceProvider; use Monolog\Handler\TestHandler; use Monolog\Logger; @@ -19,10 +20,8 @@ use Monolog\Logger; * MonologTrait test cases. * * @author Fabien Potencier - * - * @requires PHP 5.4 */ -class MonologTraitTest extends \PHPUnit_Framework_TestCase +class MonologTraitTest extends TestCase { public function testLog() { @@ -38,9 +37,10 @@ class MonologTraitTest extends \PHPUnit_Framework_TestCase { $app = new MonologApplication(); $app->register(new MonologServiceProvider(), array( - 'monolog.handler' => $app->share(function () use ($app) { + 'monolog.handler' => function () use ($app) { return new TestHandler($app['monolog.level']); - }), + }, + 'monolog.logfile' => 'php://memory', )); return $app; diff --git a/lib/silex/vendor/silex/silex/tests/Silex/Tests/Application/SecurityTraitTest.php b/lib/silex/vendor/silex/silex/tests/Silex/Tests/Application/SecurityTraitTest.php index d1b07a19d..71cb3af62 100644 --- a/lib/silex/vendor/silex/silex/tests/Silex/Tests/Application/SecurityTraitTest.php +++ b/lib/silex/vendor/silex/silex/tests/Silex/Tests/Application/SecurityTraitTest.php @@ -11,6 +11,7 @@ namespace Silex\Tests\Application; +use PHPUnit\Framework\TestCase; use Silex\Provider\SecurityServiceProvider; use Symfony\Component\Security\Core\User\User; use Symfony\Component\HttpFoundation\Request; @@ -19,89 +20,68 @@ use Symfony\Component\HttpFoundation\Request; * SecurityTrait test cases. * * @author Fabien Potencier - * - * @requires PHP 5.4 */ -class SecurityTraitTest extends \PHPUnit_Framework_TestCase +class SecurityTraitTest extends TestCase { - public function testUser() + public function testEncodePassword() + { + $app = $this->createApplication(array( + 'fabien' => array('ROLE_ADMIN', '$2y$15$lzUNsTegNXvZW3qtfucV0erYBcEqWVeyOmjolB7R1uodsAVJ95vvu'), + )); + + $user = new User('foo', 'bar'); + $password = 'foo'; + $encoded = $app->encodePassword($user, $password); + + $this->assertTrue( + $app['security.encoder_factory']->getEncoder($user)->isPasswordValid($encoded, $password, $user->getSalt()) + ); + } + + /** + * @expectedException \Symfony\Component\Security\Core\Exception\AuthenticationCredentialsNotFoundException + */ + public function testIsGrantedWithoutTokenThrowsException() + { + $app = $this->createApplication(); + $app->get('/', function () { return 'foo'; }); + $app->handle(Request::create('/')); + $app->isGranted('ROLE_ADMIN'); + } + + public function testIsGranted() { $request = Request::create('/'); - $app = $this->createApplication(); + $app = $this->createApplication(array( + 'fabien' => array('ROLE_ADMIN', '$2y$15$lzUNsTegNXvZW3qtfucV0erYBcEqWVeyOmjolB7R1uodsAVJ95vvu'), + 'monique' => array('ROLE_USER', '$2y$15$lzUNsTegNXvZW3qtfucV0erYBcEqWVeyOmjolB7R1uodsAVJ95vvu'), + )); $app->get('/', function () { return 'foo'; }); - $app->handle($request); - $this->assertNull($app->user()); + // User is Monique (ROLE_USER) + $request->headers->set('PHP_AUTH_USER', 'monique'); + $request->headers->set('PHP_AUTH_PW', 'foo'); + $app->handle($request); + $this->assertTrue($app->isGranted('ROLE_USER')); + $this->assertFalse($app->isGranted('ROLE_ADMIN')); + + // User is Fabien (ROLE_ADMIN) $request->headers->set('PHP_AUTH_USER', 'fabien'); $request->headers->set('PHP_AUTH_PW', 'foo'); $app->handle($request); - $this->assertInstanceOf('Symfony\Component\Security\Core\User\UserInterface', $app->user()); - $this->assertEquals('fabien', $app->user()->getUsername()); + $this->assertFalse($app->isGranted('ROLE_USER')); + $this->assertTrue($app->isGranted('ROLE_ADMIN')); } - public function testUserWithNoToken() - { - $request = Request::create('/'); - - $app = new SecurityApplication(); - $app['security'] = $this->getMockBuilder('Symfony\Component\Security\Core\SecurityContext') - ->disableOriginalConstructor() - ->getMock(); - - $app['security']->expects($this->any()) - ->method('getToken') - ->will($this->returnValue(null)); - - $app->get('/', function () { return 'foo'; }); - $app->handle($request); - $this->assertNull($app->user()); - } - - public function testUserWithInvalidUser() - { - $request = Request::create('/'); - - $app = new SecurityApplication(); - $app['security'] = $this->getMockBuilder('Symfony\Component\Security\Core\SecurityContext') - ->disableOriginalConstructor() - ->getMock(); - - $token = $this->getMockBuilder('Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken') - ->disableOriginalConstructor() - ->getMock(); - - $token->expects($this->once()) - ->method('getUser') - ->will($this->returnValue(array())); - - $app['security']->expects($this->any()) - ->method('getToken') - ->will($this->returnValue($token)); - - $app->get('/', function () { return 'foo'; }); - $app->handle($request); - $this->assertNull($app->user()); - } - - public function testEncodePassword() - { - $app = $this->createApplication(); - - $user = new User('foo', 'bar'); - $this->assertEquals('5FZ2Z8QIkA7UTZ4BYkoC+GsReLf569mSKDsfods6LYQ8t+a8EW9oaircfMpmaLbPBh4FOBiiFyLfuZmTSUwzZg==', $app->encodePassword($user, 'foo')); - } - - public function createApplication() + public function createApplication($users = array()) { $app = new SecurityApplication(); $app->register(new SecurityServiceProvider(), array( 'security.firewalls' => array( 'default' => array( 'http' => true, - 'users' => array( - 'fabien' => array('ROLE_ADMIN', '5FZ2Z8QIkA7UTZ4BYkoC+GsReLf569mSKDsfods6LYQ8t+a8EW9oaircfMpmaLbPBh4FOBiiFyLfuZmTSUwzZg=='), - ), + 'users' => $users, ), ), )); diff --git a/lib/silex/vendor/silex/silex/tests/Silex/Tests/Application/SwiftmailerTraitTest.php b/lib/silex/vendor/silex/silex/tests/Silex/Tests/Application/SwiftmailerTraitTest.php index a37725e9a..34620b719 100644 --- a/lib/silex/vendor/silex/silex/tests/Silex/Tests/Application/SwiftmailerTraitTest.php +++ b/lib/silex/vendor/silex/silex/tests/Silex/Tests/Application/SwiftmailerTraitTest.php @@ -11,16 +11,15 @@ namespace Silex\Tests\Application; +use PHPUnit\Framework\TestCase; use Silex\Provider\SwiftmailerServiceProvider; /** * SwiftmailerTrait test cases. * * @author Fabien Potencier - * - * @requires PHP 5.4 */ -class SwiftmailerTraitTest extends \PHPUnit_Framework_TestCase +class SwiftmailerTraitTest extends TestCase { public function testMail() { diff --git a/lib/silex/vendor/silex/silex/tests/Silex/Tests/Application/TranslationTraitTest.php b/lib/silex/vendor/silex/silex/tests/Silex/Tests/Application/TranslationTraitTest.php index af2d8a417..5c0546002 100644 --- a/lib/silex/vendor/silex/silex/tests/Silex/Tests/Application/TranslationTraitTest.php +++ b/lib/silex/vendor/silex/silex/tests/Silex/Tests/Application/TranslationTraitTest.php @@ -11,16 +11,15 @@ namespace Silex\Tests\Application; +use PHPUnit\Framework\TestCase; use Silex\Provider\TranslationServiceProvider; /** * TranslationTrait test cases. * * @author Fabien Potencier - * - * @requires PHP 5.4 */ -class TranslationTraitTest extends \PHPUnit_Framework_TestCase +class TranslationTraitTest extends TestCase { public function testTrans() { diff --git a/lib/silex/vendor/silex/silex/tests/Silex/Tests/Application/TwigTraitTest.php b/lib/silex/vendor/silex/silex/tests/Silex/Tests/Application/TwigTraitTest.php index 70710cd5c..250ebcfae 100644 --- a/lib/silex/vendor/silex/silex/tests/Silex/Tests/Application/TwigTraitTest.php +++ b/lib/silex/vendor/silex/silex/tests/Silex/Tests/Application/TwigTraitTest.php @@ -11,6 +11,7 @@ namespace Silex\Tests\Application; +use PHPUnit\Framework\TestCase; use Silex\Provider\TwigServiceProvider; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\StreamedResponse; @@ -19,10 +20,8 @@ use Symfony\Component\HttpFoundation\StreamedResponse; * TwigTrait test cases. * * @author Fabien Potencier - * - * @requires PHP 5.4 */ -class TwigTraitTest extends \PHPUnit_Framework_TestCase +class TwigTraitTest extends TestCase { public function testRender() { diff --git a/lib/silex/vendor/silex/silex/tests/Silex/Tests/Application/UrlGeneratorTraitTest.php b/lib/silex/vendor/silex/silex/tests/Silex/Tests/Application/UrlGeneratorTraitTest.php index 16b11f5ff..df29d6e79 100644 --- a/lib/silex/vendor/silex/silex/tests/Silex/Tests/Application/UrlGeneratorTraitTest.php +++ b/lib/silex/vendor/silex/silex/tests/Silex/Tests/Application/UrlGeneratorTraitTest.php @@ -11,38 +11,29 @@ namespace Silex\Tests\Application; -use Silex\Provider\UrlGeneratorServiceProvider; +use PHPUnit\Framework\TestCase; +use Symfony\Component\Routing\Generator\UrlGeneratorInterface; /** * UrlGeneratorTrait test cases. * * @author Fabien Potencier - * - * @requires PHP 5.4 */ -class UrlGeneratorTraitTest extends \PHPUnit_Framework_TestCase +class UrlGeneratorTraitTest extends TestCase { public function testUrl() { - $app = $this->createApplication(); - $app['url_generator'] = $translator = $this->getMockBuilder('Symfony\Component\Routing\Generator\UrlGeneratorInterface')->disableOriginalConstructor()->getMock(); - $translator->expects($this->once())->method('generate')->with('foo', array(), true); + $app = new UrlGeneratorApplication(); + $app['url_generator'] = $this->getMockBuilder('Symfony\Component\Routing\Generator\UrlGeneratorInterface')->disableOriginalConstructor()->getMock(); + $app['url_generator']->expects($this->once())->method('generate')->with('foo', array(), UrlGeneratorInterface::ABSOLUTE_URL); $app->url('foo'); } public function testPath() { - $app = $this->createApplication(); - $app['url_generator'] = $translator = $this->getMockBuilder('Symfony\Component\Routing\Generator\UrlGeneratorInterface')->disableOriginalConstructor()->getMock(); - $translator->expects($this->once())->method('generate')->with('foo', array(), false); + $app = new UrlGeneratorApplication(); + $app['url_generator'] = $this->getMockBuilder('Symfony\Component\Routing\Generator\UrlGeneratorInterface')->disableOriginalConstructor()->getMock(); + $app['url_generator']->expects($this->once())->method('generate')->with('foo', array(), UrlGeneratorInterface::ABSOLUTE_PATH); $app->path('foo'); } - - public function createApplication() - { - $app = new UrlGeneratorApplication(); - $app->register(new UrlGeneratorServiceProvider()); - - return $app; - } } diff --git a/lib/silex/vendor/silex/silex/tests/Silex/Tests/ApplicationTest.php b/lib/silex/vendor/silex/silex/tests/Silex/Tests/ApplicationTest.php index 7158db3b8..bcf202955 100644 --- a/lib/silex/vendor/silex/silex/tests/Silex/Tests/ApplicationTest.php +++ b/lib/silex/vendor/silex/silex/tests/Silex/Tests/ApplicationTest.php @@ -11,25 +11,28 @@ namespace Silex\Tests; +use Fig\Link\GenericLinkProvider; +use Fig\Link\Link; +use PHPUnit\Framework\TestCase; use Silex\Application; use Silex\ControllerCollection; -use Silex\ControllerProviderInterface; +use Silex\Api\ControllerProviderInterface; use Silex\Route; -use Silex\Provider\MonologServiceProvider; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpKernel\Exception\HttpException; -use Symfony\Component\Debug\ErrorHandler; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\StreamedResponse; use Symfony\Component\HttpKernel\HttpKernelInterface; use Symfony\Component\EventDispatcher\Event; +use Symfony\Component\Routing\RouteCollection; +use Symfony\Component\WebLink\HttpHeaderSerializer; /** * Application test cases. * * @author Igor Wiedler */ -class ApplicationTest extends \PHPUnit_Framework_TestCase +class ApplicationTest extends TestCase { public function testMatchReturnValue() { @@ -85,7 +88,7 @@ class ApplicationTest extends \PHPUnit_Framework_TestCase $routes = $app['routes']; $this->assertInstanceOf('Symfony\Component\Routing\RouteCollection', $routes); - $this->assertEquals(0, count($routes->all())); + $this->assertCount(0, $routes->all()); } public function testGetRoutesWithRoutes() @@ -102,9 +105,9 @@ class ApplicationTest extends \PHPUnit_Framework_TestCase $routes = $app['routes']; $this->assertInstanceOf('Symfony\Component\Routing\RouteCollection', $routes); - $this->assertEquals(0, count($routes->all())); + $this->assertCount(0, $routes->all()); $app->flush(); - $this->assertEquals(2, count($routes->all())); + $this->assertCount(2, $routes->all()); } public function testOnCoreController() @@ -176,12 +179,36 @@ class ApplicationTest extends \PHPUnit_Framework_TestCase public function testControllersAsMethods() { $app = new Application(); + unset($app['exception_handler']); $app->get('/{name}', 'Silex\Tests\FooController::barAction'); $this->assertEquals('Hello Fabien', $app->handle(Request::create('/Fabien'))->getContent()); } + public function testApplicationTypeHintWorks() + { + $app = new SpecialApplication(); + unset($app['exception_handler']); + + $app->get('/{name}', 'Silex\Tests\FooController::barSpecialAction'); + + $this->assertEquals('Hello Fabien in Silex\Tests\SpecialApplication', $app->handle(Request::create('/Fabien'))->getContent()); + } + + /** + * @requires PHP 7.0 + */ + public function testPhp7TypeHintWorks() + { + $app = new SpecialApplication(); + unset($app['exception_handler']); + + $app->get('/{name}', 'Silex\Tests\Fixtures\Php7Controller::typehintedAction'); + + $this->assertEquals('Hello Fabien in Silex\Tests\SpecialApplication', $app->handle(Request::create('/Fabien'))->getContent()); + } + public function testHttpSpec() { $app = new Application(); @@ -417,30 +444,6 @@ class ApplicationTest extends \PHPUnit_Framework_TestCase $app->handle(Request::create('/'), HttpKernelInterface::MASTER_REQUEST, false); } - /** - * @expectedException \RuntimeException - */ - public function testAccessingRequestOutsideOfScopeShouldThrowRuntimeException() - { - $app = new Application(); - - $request = $app['request']; - } - - /** - * @expectedException \RuntimeException - */ - public function testAccessingRequestOutsideOfScopeShouldThrowRuntimeExceptionAfterHandling() - { - $app = new Application(); - $app->get('/', function () { - return 'hello'; - }); - $app->handle(Request::create('/'), HttpKernelInterface::MASTER_REQUEST, false); - - $request = $app['request']; - } - public function testSubRequest() { $app = new Application(); @@ -454,31 +457,10 @@ class ApplicationTest extends \PHPUnit_Framework_TestCase $this->assertEquals('foo', $app->handle(Request::create('/'))->getContent()); } - public function testSubRequestDoesNotReplaceMainRequestAfterHandling() - { - $mainRequest = Request::create('/'); - $subRequest = Request::create('/sub'); - - $app = new Application(); - $app->get('/sub', function (Request $request) { - return new Response('foo'); - }); - $app->get('/', function (Request $request) use ($subRequest, $app) { - $response = $app->handle($subRequest, HttpKernelInterface::SUB_REQUEST); - - // request in app must be the main request here - $response->setContent($response->getContent().' '.$app['request']->getPathInfo()); - - return $response; - }); - - $this->assertEquals('foo /', $app->handle($mainRequest)->getContent()); - } - public function testRegisterShouldReturnSelf() { $app = new Application(); - $provider = $this->getMock('Silex\ServiceProviderInterface'); + $provider = $this->getMockBuilder('Pimple\ServiceProviderInterface')->getMock(); $this->assertSame($app, $app->register($provider)); } @@ -508,7 +490,7 @@ class ApplicationTest extends \PHPUnit_Framework_TestCase /** * @expectedException \LogicException - * @expectedExceptionMessage The "mount" method takes either a "ControllerCollection" or a "ControllerProviderInterface" instance. + * @expectedExceptionMessage The "mount" method takes either a "ControllerCollection" instance, "ControllerProviderInterface" instance, or a callable. */ public function testMountNullException() { @@ -526,6 +508,18 @@ class ApplicationTest extends \PHPUnit_Framework_TestCase $app->mount('/exception', new IncorrectControllerCollection()); } + public function testMountCallable() + { + $app = new Application(); + $app->mount('/prefix', function (ControllerCollection $coll) { + $coll->get('/path'); + }); + + $app->flush(); + + $this->assertEquals(1, $app['routes']->count()); + } + public function testSendFile() { $app = new Application(); @@ -542,22 +536,162 @@ class ApplicationTest extends \PHPUnit_Framework_TestCase public function testGetRouteCollectionWithRouteWithoutController() { $app = new Application(); - $app['exception_handler']->disable(); + unset($app['exception_handler']); $app->match('/')->bind('homepage'); $app->handle(Request::create('/')); } - public function testRedirectDoesNotRaisePHPNoticesWhenMonologIsRegistered() + public function testBeforeFilterOnMountedControllerGroupIsolatedToGroup() { $app = new Application(); + $app->match('/', function () { return new Response('ok'); }); + $mounted = $app['controllers_factory']; + $mounted->before(function () { return new Response('not ok'); }); + $app->mount('/group', $mounted); - ErrorHandler::register(); - $app['monolog.logfile'] = 'php://memory'; - $app->register(new MonologServiceProvider()); - $app->get('/foo/', function () { return 'ok'; }); + $response = $app->handle(Request::create('/')); + $this->assertEquals('ok', $response->getContent()); + } + + public function testViewListenerWithPrimitive() + { + $app = new Application(); + $app->get('/foo', function () { return 123; }); + $app->view(function ($view, Request $request) { + return new Response($view); + }); $response = $app->handle(Request::create('/foo')); - $this->assertEquals(301, $response->getStatusCode()); + + $this->assertEquals('123', $response->getContent()); + } + + public function testViewListenerWithArrayTypeHint() + { + $app = new Application(); + $app->get('/foo', function () { return array('ok'); }); + $app->view(function (array $view) { + return new Response($view[0]); + }); + + $response = $app->handle(Request::create('/foo')); + + $this->assertEquals('ok', $response->getContent()); + } + + public function testViewListenerWithObjectTypeHint() + { + $app = new Application(); + $app->get('/foo', function () { return (object) array('name' => 'world'); }); + $app->view(function (\stdClass $view) { + return new Response('Hello '.$view->name); + }); + + $response = $app->handle(Request::create('/foo')); + + $this->assertEquals('Hello world', $response->getContent()); + } + + public function testViewListenerWithCallableTypeHint() + { + $app = new Application(); + $app->get('/foo', function () { return function () { return 'world'; }; }); + $app->view(function (callable $view) { + return new Response('Hello '.$view()); + }); + + $response = $app->handle(Request::create('/foo')); + + $this->assertEquals('Hello world', $response->getContent()); + } + + public function testViewListenersCanBeChained() + { + $app = new Application(); + $app->get('/foo', function () { return (object) array('name' => 'world'); }); + + $app->view(function (\stdClass $view) { + return array('msg' => 'Hello '.$view->name); + }); + + $app->view(function (array $view) { + return $view['msg']; + }); + + $response = $app->handle(Request::create('/foo')); + + $this->assertEquals('Hello world', $response->getContent()); + } + + public function testViewListenersAreIgnoredIfNotSuitable() + { + $app = new Application(); + $app->get('/foo', function () { return 'Hello world'; }); + + $app->view(function (\stdClass $view) { + throw new \Exception('View listener was called'); + }); + + $app->view(function (array $view) { + throw new \Exception('View listener was called'); + }); + + $response = $app->handle(Request::create('/foo')); + + $this->assertEquals('Hello world', $response->getContent()); + } + + public function testViewListenersResponsesAreNotUsedIfNull() + { + $app = new Application(); + $app->get('/foo', function () { return 'Hello world'; }); + + $app->view(function ($view) { + return 'Hello view listener'; + }); + + $app->view(function ($view) { + return; + }); + + $response = $app->handle(Request::create('/foo')); + + $this->assertEquals('Hello view listener', $response->getContent()); + } + + public function testWebLinkListener() + { + if (!class_exists(HttpHeaderSerializer::class)) { + self::markTestSkipped('Symfony WebLink component is required.'); + } + + $app = new Application(); + + $app->get('/', function () { + return 'hello'; + }); + + $request = Request::create('/'); + $request->attributes->set('_links', (new GenericLinkProvider())->withLink(new Link('preload', '/foo.css'))); + + $response = $app->handle($request); + + $this->assertEquals('; rel="preload"', $response->headers->get('Link')); + } + + public function testDefaultRoutesFactory() + { + $app = new Application(); + $this->assertInstanceOf('Symfony\Component\Routing\RouteCollection', $app['routes']); + } + + public function testOverriddenRoutesFactory() + { + $app = new Application(); + $app['routes_factory'] = $app->factory(function () { + return new RouteCollectionSubClass(); + }); + $this->assertInstanceOf('Silex\Tests\RouteCollectionSubClass', $app['routes']); } } @@ -567,6 +701,11 @@ class FooController { return 'Hello '.$app->escape($name); } + + public function barSpecialAction(SpecialApplication $app, $name) + { + return 'Hello '.$app->escape($name).' in '.get_class($app); + } } class IncorrectControllerCollection implements ControllerProviderInterface @@ -576,3 +715,11 @@ class IncorrectControllerCollection implements ControllerProviderInterface return; } } + +class RouteCollectionSubClass extends RouteCollection +{ +} + +class SpecialApplication extends Application +{ +} diff --git a/lib/silex/vendor/silex/silex/tests/Silex/Tests/CallbackResolverTest.php b/lib/silex/vendor/silex/silex/tests/Silex/Tests/CallbackResolverTest.php index cfc7776bb..b637a944e 100644 --- a/lib/silex/vendor/silex/silex/tests/Silex/Tests/CallbackResolverTest.php +++ b/lib/silex/vendor/silex/silex/tests/Silex/Tests/CallbackResolverTest.php @@ -11,39 +11,72 @@ namespace Silex\Tests; +use PHPUnit\Framework\TestCase; +use Pimple\Container; use Silex\CallbackResolver; -class CallbackResolverTest extends \PHPUnit_Framework_Testcase +class CallbackResolverTest extends Testcase { + private $app; + private $resolver; + public function setup() { - $this->app = new \Pimple(); + $this->app = new Container(); $this->resolver = new CallbackResolver($this->app); } public function testShouldResolveCallback() { - $this->app['some_service'] = function () { return new \stdClass(); }; + $callable = function () {}; + $this->app['some_service'] = function () { return new \ArrayObject(); }; + $this->app['callable_service'] = function () use ($callable) { + return $callable; + }; $this->assertTrue($this->resolver->isValid('some_service:methodName')); + $this->assertTrue($this->resolver->isValid('callable_service')); $this->assertEquals( - array($this->app['some_service'], 'methodName'), - $this->resolver->convertCallback('some_service:methodName') + array($this->app['some_service'], 'append'), + $this->resolver->convertCallback('some_service:append') ); + $this->assertSame($callable, $this->resolver->convertCallback('callable_service')); } - public function testNonStringsAreNotValid() + /** + * @dataProvider nonStringsAreNotValidProvider + */ + public function testNonStringsAreNotValid($name) { - $this->assertFalse($this->resolver->isValid(null)); - $this->assertFalse($this->resolver->isValid('some_service::methodName')); + $this->assertFalse($this->resolver->isValid($name)); + } + + public function nonStringsAreNotValidProvider() + { + return array( + array(null), + array('some_service::methodName'), + array('missing_service'), + ); } /** * @expectedException \InvalidArgumentException - * @expectedExceptionMessage Service "some_service" does not exist. + * @expectedExceptionMessageRegExp /Service "[a-z_]+" is not callable./ + * @dataProvider shouldThrowAnExceptionIfServiceIsNotCallableProvider */ - public function testShouldThrowAnExceptionIfServiceIsMissing() + public function testShouldThrowAnExceptionIfServiceIsNotCallable($name) { - $this->resolver->convertCallback('some_service:methodName'); + $this->app['non_callable_obj'] = function () { return new \stdClass(); }; + $this->app['non_callable'] = function () { return array(); }; + $this->resolver->convertCallback($name); + } + + public function shouldThrowAnExceptionIfServiceIsNotCallableProvider() + { + return array( + array('non_callable_obj:methodA'), + array('non_callable'), + ); } } diff --git a/lib/silex/vendor/silex/silex/tests/Silex/Tests/CallbackServicesTest.php b/lib/silex/vendor/silex/silex/tests/Silex/Tests/CallbackServicesTest.php index 5e9113048..915c04965 100644 --- a/lib/silex/vendor/silex/silex/tests/Silex/Tests/CallbackServicesTest.php +++ b/lib/silex/vendor/silex/silex/tests/Silex/Tests/CallbackServicesTest.php @@ -11,6 +11,7 @@ namespace Silex\Tests; +use PHPUnit\Framework\TestCase; use Silex\Application; use Symfony\Component\HttpFoundation\Request; use Silex\Provider\ServiceControllerServiceProvider; @@ -20,7 +21,7 @@ use Silex\Provider\ServiceControllerServiceProvider; * * @author Fabien Potencier */ -class CallbackServicesTest extends \PHPUnit_Framework_TestCase +class CallbackServicesTest extends TestCase { public $called = array(); @@ -29,9 +30,9 @@ class CallbackServicesTest extends \PHPUnit_Framework_TestCase $app = new Application(); $app->register(new ServiceControllerServiceProvider()); - $app['service'] = $app->share(function () { + $app['service'] = function () { return new CallbackServicesTest(); - }); + }; $app->before('service:beforeApp'); $app->after('service:afterApp'); diff --git a/lib/silex/vendor/silex/silex/tests/Silex/Tests/ControllerCollectionTest.php b/lib/silex/vendor/silex/silex/tests/Silex/Tests/ControllerCollectionTest.php index f1ecdaa79..9b96e00c7 100644 --- a/lib/silex/vendor/silex/silex/tests/Silex/Tests/ControllerCollectionTest.php +++ b/lib/silex/vendor/silex/silex/tests/Silex/Tests/ControllerCollectionTest.php @@ -11,23 +11,26 @@ namespace Silex\Tests; +use PHPUnit\Framework\TestCase; +use Silex\Application; use Silex\Controller; use Silex\ControllerCollection; use Silex\Exception\ControllerFrozenException; use Silex\Route; +use Symfony\Component\Routing\RouteCollection; /** * ControllerCollection test cases. * * @author Igor Wiedler */ -class ControllerCollectionTest extends \PHPUnit_Framework_TestCase +class ControllerCollectionTest extends TestCase { public function testGetRouteCollectionWithNoRoutes() { $controllers = new ControllerCollection(new Route()); $routes = $controllers->flush(); - $this->assertEquals(0, count($routes->all())); + $this->assertCount(0, $routes->all()); } public function testGetRouteCollectionWithRoutes() @@ -37,7 +40,7 @@ class ControllerCollectionTest extends \PHPUnit_Framework_TestCase $controllers->match('/bar', function () {}); $routes = $controllers->flush(); - $this->assertEquals(2, count($routes->all())); + $this->assertCount(2, $routes->all()); } public function testControllerFreezing() @@ -69,7 +72,7 @@ class ControllerCollectionTest extends \PHPUnit_Framework_TestCase $mountedRootController = $controllers->match('/', function () {}); $mainRootController = new Controller(new Route('/')); - $mainRootController->bind($mainRootController->generateRouteName('main_')); + $mainRootController->bind($mainRootController->generateRouteName('main_1')); $controllers->flush(); @@ -82,11 +85,98 @@ class ControllerCollectionTest extends \PHPUnit_Framework_TestCase $controllers->match('/a-a', function () {}); $controllers->match('/a_a', function () {}); + $controllers->match('/a/a', function () {}); + + $routes = $controllers->flush(); + + $this->assertCount(3, $routes->all()); + $this->assertEquals(array('_a_a', '_a_a_1', '_a_a_2'), array_keys($routes->all())); + } + + public function testUniqueGeneratedRouteNamesAmongMounts() + { + $controllers = new ControllerCollection(new Route()); + + $controllers->mount('/root-a', $rootA = new ControllerCollection(new Route())); + $controllers->mount('/root_a', $rootB = new ControllerCollection(new Route())); + + $rootA->match('/leaf', function () {}); + $rootB->match('/leaf', function () {}); $routes = $controllers->flush(); $this->assertCount(2, $routes->all()); - $this->assertEquals(array('_a_a', '_a_a_'), array_keys($routes->all())); + $this->assertEquals(array('_root_a_leaf', '_root_a_leaf_1'), array_keys($routes->all())); + } + + public function testUniqueGeneratedRouteNamesAmongNestedMounts() + { + $controllers = new ControllerCollection(new Route()); + + $controllers->mount('/root-a', $rootA = new ControllerCollection(new Route())); + $controllers->mount('/root_a', $rootB = new ControllerCollection(new Route())); + + $rootA->mount('/tree', $treeA = new ControllerCollection(new Route())); + $rootB->mount('/tree', $treeB = new ControllerCollection(new Route())); + + $treeA->match('/leaf', function () {}); + $treeB->match('/leaf', function () {}); + + $routes = $controllers->flush(); + + $this->assertCount(2, $routes->all()); + $this->assertEquals(array('_root_a_tree_leaf', '_root_a_tree_leaf_1'), array_keys($routes->all())); + } + + public function testMountCallable() + { + $controllers = new ControllerCollection(new Route()); + $controllers->mount('/prefix', function (ControllerCollection $coll) { + $coll->mount('/path', function ($coll) { + $coll->get('/part'); + }); + }); + + $routes = $controllers->flush(); + $this->assertEquals('/prefix/path/part', current($routes->all())->getPath()); + } + + public function testMountCallableProperClone() + { + $controllers = new ControllerCollection(new Route(), new RouteCollection()); + $controllers->get('/'); + + $subControllers = null; + $controllers->mount('/prefix', function (ControllerCollection $coll) use (&$subControllers) { + $subControllers = $coll; + $coll->get('/'); + }); + + $routes = $controllers->flush(); + $subRoutes = $subControllers->flush(); + $this->assertTrue($routes->count() == 2 && $subRoutes->count() == 0); + } + + public function testMountControllersFactory() + { + $testControllers = new ControllerCollection(new Route()); + $controllers = new ControllerCollection(new Route(), null, function () use ($testControllers) { + return $testControllers; + }); + + $controllers->mount('/prefix', function ($mounted) use ($testControllers) { + $this->assertSame($mounted, $testControllers); + }); + } + + /** + * @expectedException \LogicException + * @expectedExceptionMessage The "mount" method takes either a "ControllerCollection" instance or callable. + */ + public function testMountCallableException() + { + $controllers = new ControllerCollection(new Route()); + $controllers->mount('/prefix', ''); } public function testAssert() @@ -101,6 +191,23 @@ class ControllerCollectionTest extends \PHPUnit_Framework_TestCase $this->assertEquals('\w+', $controller->getRoute()->getRequirement('extra')); } + public function testAssertWithMountCallable() + { + $controllers = new ControllerCollection(new Route()); + $controller = null; + $controllers->mount('/{name}', function ($mounted) use (&$controller) { + $mounted->assert('name', '\w+'); + $mounted->mount('/{id}', function ($mounted2) use (&$controller) { + $mounted2->assert('id', '\d+'); + $controller = $mounted2->match('/{extra}', function () {})->assert('extra', '\w+'); + }); + }); + + $this->assertEquals('\d+', $controller->getRoute()->getRequirement('id')); + $this->assertEquals('\w+', $controller->getRoute()->getRequirement('name')); + $this->assertEquals('\w+', $controller->getRoute()->getRequirement('extra')); + } + public function testValue() { $controllers = new ControllerCollection(new Route()); @@ -129,11 +236,11 @@ class ControllerCollectionTest extends \PHPUnit_Framework_TestCase $controllers->requireHttp(); $controller = $controllers->match('/{id}/{name}/{extra}', function () {})->requireHttps(); - $this->assertEquals('https', $controller->getRoute()->getRequirement('_scheme')); + $this->assertEquals(array('https'), $controller->getRoute()->getSchemes()); $controllers->requireHttp(); - $this->assertEquals('http', $controller->getRoute()->getRequirement('_scheme')); + $this->assertEquals(array('http'), $controller->getRoute()->getSchemes()); } public function testBefore() @@ -156,6 +263,14 @@ class ControllerCollectionTest extends \PHPUnit_Framework_TestCase $this->assertEquals(array('mid1', 'mid2', 'mid3'), $controller->getRoute()->getOption('_after_middlewares')); } + public function testWhen() + { + $controllers = new ControllerCollection(new Route()); + $controller = $controllers->match('/{id}/{name}/{extra}', function () {})->when('request.isSecure() == true'); + + $this->assertEquals('request.isSecure() == true', $controller->getRoute()->getCondition()); + } + public function testRouteExtension() { $route = new MyRoute1(); @@ -176,6 +291,43 @@ class ControllerCollectionTest extends \PHPUnit_Framework_TestCase $controller = new ControllerCollection($route); $controller->bar(); } + + public function testNestedCollectionRouteCallbacks() + { + $cl1 = new ControllerCollection(new MyRoute1()); + $cl2 = new ControllerCollection(new MyRoute1()); + + $c1 = $cl2->match('/c1', function () {}); + $cl1->mount('/foo', $cl2); + $c2 = $cl2->match('/c2', function () {}); + $cl1->before('before'); + $c3 = $cl2->match('/c3', function () {}); + + $cl1->flush(); + + $this->assertEquals(array('before'), $c1->getRoute()->getOption('_before_middlewares')); + $this->assertEquals(array('before'), $c2->getRoute()->getOption('_before_middlewares')); + $this->assertEquals(array('before'), $c3->getRoute()->getOption('_before_middlewares')); + } + + public function testRoutesFactoryOmitted() + { + $controllers = new ControllerCollection(new Route()); + $routes = $controllers->flush(); + $this->assertInstanceOf('Symfony\Component\Routing\RouteCollection', $routes); + } + + public function testRoutesFactoryInConstructor() + { + $app = new Application(); + $app['routes_factory'] = $app->factory(function () { + return new RouteCollectionSubClass2(); + }); + + $controllers = new ControllerCollection(new Route(), $app['routes_factory']); + $routes = $controllers->flush(); + $this->assertInstanceOf('Silex\Tests\RouteCollectionSubClass2', $routes); + } } class MyRoute1 extends Route @@ -187,3 +339,7 @@ class MyRoute1 extends Route $this->foo = $value; } } + +class RouteCollectionSubClass2 extends RouteCollection +{ +} diff --git a/lib/silex/vendor/silex/silex/tests/Silex/Tests/ControllerResolverTest.php b/lib/silex/vendor/silex/silex/tests/Silex/Tests/ControllerResolverTest.php index 120aad575..14ccbbdf7 100644 --- a/lib/silex/vendor/silex/silex/tests/Silex/Tests/ControllerResolverTest.php +++ b/lib/silex/vendor/silex/silex/tests/Silex/Tests/ControllerResolverTest.php @@ -11,19 +11,28 @@ namespace Silex\Tests; +use PHPUnit\Framework\TestCase; use Silex\ControllerResolver; use Silex\Application; use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpKernel\Kernel; /** * ControllerResolver test cases. * * @author Fabien Potencier */ -class ControllerResolverTest extends \PHPUnit_Framework_TestCase +class ControllerResolverTest extends TestCase { + /** + * @group legacy + */ public function testGetArguments() { + if (Kernel::VERSION_ID >= 30100) { + self::markTestSkipped('HttpKernel < 3.1.0 is required'); + } + $app = new Application(); $resolver = new ControllerResolver($app); diff --git a/lib/silex/vendor/silex/silex/tests/Silex/Tests/ControllerTest.php b/lib/silex/vendor/silex/silex/tests/Silex/Tests/ControllerTest.php index 6bd0be18c..88c6f0a11 100644 --- a/lib/silex/vendor/silex/silex/tests/Silex/Tests/ControllerTest.php +++ b/lib/silex/vendor/silex/silex/tests/Silex/Tests/ControllerTest.php @@ -11,6 +11,7 @@ namespace Silex\Tests; +use PHPUnit\Framework\TestCase; use Silex\Controller; use Silex\Route; @@ -19,7 +20,7 @@ use Silex\Route; * * @author Igor Wiedler */ -class ControllerTest extends \PHPUnit_Framework_TestCase +class ControllerTest extends TestCase { public function testBind() { @@ -80,10 +81,10 @@ class ControllerTest extends \PHPUnit_Framework_TestCase /** * @dataProvider provideRouteAndExpectedRouteName */ - public function testDefaultRouteNameGeneration(Route $route, $expectedRouteName) + public function testDefaultRouteNameGeneration(Route $route, $prefix, $expectedRouteName) { $controller = new Controller($route); - $controller->bind($controller->generateRouteName('')); + $controller->bind($controller->generateRouteName($prefix)); $this->assertEquals($expectedRouteName, $controller->getRouteName()); } @@ -91,10 +92,11 @@ class ControllerTest extends \PHPUnit_Framework_TestCase public function provideRouteAndExpectedRouteName() { return array( - array(new Route('/Invalid%Symbols#Stripped', array(), array('_method' => 'POST')), 'POST_InvalidSymbolsStripped'), - array(new Route('/post/{id}', array(), array('_method' => 'GET')), 'GET_post_id'), - array(new Route('/colon:pipe|dashes-escaped'), '_colon_pipe_dashes_escaped'), - array(new Route('/underscores_and.periods'), '_underscores_and.periods'), + array(new Route('/Invalid%Symbols#Stripped', array(), array(), array(), '', array(), array('POST')), '', 'POST_InvalidSymbolsStripped'), + array(new Route('/post/{id}', array(), array(), array(), '', array(), array('GET')), '', 'GET_post_id'), + array(new Route('/colon:pipe|dashes-escaped'), '', '_colon_pipe_dashes_escaped'), + array(new Route('/underscores_and.periods'), '', '_underscores_and.periods'), + array(new Route('/post/{id}', array(), array(), array(), '', array(), array('GET')), 'prefix', 'GET_prefix_post_id'), ); } diff --git a/lib/silex/vendor/silex/silex/tests/Silex/Tests/EventListener/LogListenerTest.php b/lib/silex/vendor/silex/silex/tests/Silex/Tests/EventListener/LogListenerTest.php index 85cd94c76..b56082e09 100644 --- a/lib/silex/vendor/silex/silex/tests/Silex/Tests/EventListener/LogListenerTest.php +++ b/lib/silex/vendor/silex/silex/tests/Silex/Tests/EventListener/LogListenerTest.php @@ -11,6 +11,8 @@ namespace Silex\Tests\EventListener; +use PHPUnit\Framework\TestCase; +use Psr\Log\LogLevel; use Silex\EventListener\LogListener; use Symfony\Component\HttpKernel\Event\GetResponseEvent; use Symfony\Component\HttpKernel\Event\FilterResponseEvent; @@ -23,25 +25,25 @@ use Symfony\Component\HttpKernel\HttpKernelInterface; use Symfony\Component\HttpKernel\Exception\HttpException; /** - * LogListener + * LogListener. * * @author Jérôme Tamarelle */ -class LogListenerTest extends \PHPUnit_Framework_TestCase +class LogListenerTest extends TestCase { public function testRequestListener() { - $logger = $this->getMock('Psr\\Log\\LoggerInterface'); + $logger = $this->getMockBuilder('Psr\\Log\\LoggerInterface')->getMock(); $logger ->expects($this->once()) - ->method('info') - ->with($this->equalTo('> GET /foo')) + ->method('log') + ->with(LogLevel::DEBUG, '> GET /foo') ; $dispatcher = new EventDispatcher(); $dispatcher->addSubscriber(new LogListener($logger)); - $kernel = $this->getMock('Symfony\\Component\\HttpKernel\\HttpKernelInterface'); + $kernel = $this->getMockBuilder('Symfony\\Component\\HttpKernel\\HttpKernelInterface')->getMock(); $dispatcher->dispatch(KernelEvents::REQUEST, new GetResponseEvent($kernel, Request::create('/subrequest'), HttpKernelInterface::SUB_REQUEST), 'Skip sub requests'); @@ -50,17 +52,17 @@ class LogListenerTest extends \PHPUnit_Framework_TestCase public function testResponseListener() { - $logger = $this->getMock('Psr\\Log\\LoggerInterface'); + $logger = $this->getMockBuilder('Psr\\Log\\LoggerInterface')->getMock(); $logger ->expects($this->once()) - ->method('info') - ->with($this->equalTo('< 301')) + ->method('log') + ->with(LogLevel::DEBUG, '< 301') ; $dispatcher = new EventDispatcher(); $dispatcher->addSubscriber(new LogListener($logger)); - $kernel = $this->getMock('Symfony\\Component\\HttpKernel\\HttpKernelInterface'); + $kernel = $this->getMockBuilder('Symfony\\Component\\HttpKernel\\HttpKernelInterface')->getMock(); $dispatcher->dispatch(KernelEvents::RESPONSE, new FilterResponseEvent($kernel, Request::create('/foo'), HttpKernelInterface::SUB_REQUEST, Response::create('subrequest', 200)), 'Skip sub requests'); @@ -69,26 +71,24 @@ class LogListenerTest extends \PHPUnit_Framework_TestCase public function testExceptionListener() { - $logger = $this->getMock('Psr\\Log\\LoggerInterface'); + $logger = $this->getMockBuilder('Psr\\Log\\LoggerInterface')->getMock(); $logger - ->expects($this->once()) - ->method('critical') - ->with($this->equalTo('RuntimeException: Fatal error (uncaught exception) at '.__FILE__.' line '.(__LINE__+14))) + ->expects($this->at(0)) + ->method('log') + ->with(LogLevel::CRITICAL, 'RuntimeException: Fatal error (uncaught exception) at '.__FILE__.' line '.(__LINE__ + 13)) ; - $logger - ->expects($this->once()) - ->method('error') - ->with($this->equalTo('Symfony\Component\HttpKernel\Exception\HttpException: Http error (uncaught exception) at '.__FILE__.' line '.(__LINE__+10))) + ->expects($this->at(1)) + ->method('log') + ->with(LogLevel::ERROR, 'Symfony\Component\HttpKernel\Exception\HttpException: Http error (uncaught exception) at '.__FILE__.' line '.(__LINE__ + 9)) ; $dispatcher = new EventDispatcher(); $dispatcher->addSubscriber(new LogListener($logger)); - $kernel = $this->getMock('Symfony\\Component\\HttpKernel\\HttpKernelInterface'); + $kernel = $this->getMockBuilder('Symfony\\Component\\HttpKernel\\HttpKernelInterface')->getMock(); $dispatcher->dispatch(KernelEvents::EXCEPTION, new GetResponseForExceptionEvent($kernel, Request::create('/foo'), HttpKernelInterface::SUB_REQUEST, new \RuntimeException('Fatal error'))); - $dispatcher->dispatch(KernelEvents::EXCEPTION, new GetResponseForExceptionEvent($kernel, Request::create('/foo'), HttpKernelInterface::SUB_REQUEST, new HttpException(400, 'Http error'))); } } diff --git a/lib/silex/vendor/silex/silex/tests/Silex/Tests/ExceptionHandlerTest.php b/lib/silex/vendor/silex/silex/tests/Silex/Tests/ExceptionHandlerTest.php index 1f8b31928..aea31b08f 100644 --- a/lib/silex/vendor/silex/silex/tests/Silex/Tests/ExceptionHandlerTest.php +++ b/lib/silex/vendor/silex/silex/tests/Silex/Tests/ExceptionHandlerTest.php @@ -11,6 +11,7 @@ namespace Silex\Tests; +use PHPUnit\Framework\TestCase; use Silex\Application; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; @@ -21,7 +22,7 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; * * @author Igor Wiedler */ -class ExceptionHandlerTest extends \PHPUnit_Framework_TestCase +class ExceptionHandlerTest extends TestCase { public function testExceptionHandlerExceptionNoDebug() { @@ -34,7 +35,7 @@ class ExceptionHandlerTest extends \PHPUnit_Framework_TestCase $request = Request::create('/foo'); $response = $app->handle($request); - $this->assertContains('

Whoops, looks like something went wrong.

', $response->getContent()); + $this->assertContains('Whoops, looks like something went wrong.', $response->getContent()); $this->assertEquals(500, $response->getStatusCode()); } @@ -61,7 +62,7 @@ class ExceptionHandlerTest extends \PHPUnit_Framework_TestCase $request = Request::create('/foo'); $response = $app->handle($request); - $this->assertContains('

Sorry, the page you are looking for could not be found.

', $response->getContent()); + $this->assertContains('Sorry, the page you are looking for could not be found.', $response->getContent()); $this->assertEquals(404, $response->getStatusCode()); } @@ -85,7 +86,7 @@ class ExceptionHandlerTest extends \PHPUnit_Framework_TestCase $request = Request::create('/foo', 'POST'); $response = $app->handle($request); - $this->assertContains('

Whoops, looks like something went wrong.

', $response->getContent()); + $this->assertContains('Whoops, looks like something went wrong.', $response->getContent()); $this->assertEquals(405, $response->getStatusCode()); $this->assertEquals('GET', $response->headers->get('Allow')); } @@ -107,7 +108,7 @@ class ExceptionHandlerTest extends \PHPUnit_Framework_TestCase public function testNoExceptionHandler() { $app = new Application(); - $app['exception_handler']->disable(); + unset($app['exception_handler']); $app->match('/foo', function () { throw new \RuntimeException('foo exception'); @@ -162,22 +163,22 @@ class ExceptionHandlerTest extends \PHPUnit_Framework_TestCase $errors = 0; $app->error(function ($e) use (&$errors) { - $errors++; + ++$errors; }); $app->error(function ($e) use (&$errors) { - $errors++; + ++$errors; }); $app->error(function ($e) use (&$errors) { - $errors++; + ++$errors; return new Response('foo exception handler'); }); $app->error(function ($e) use (&$errors) { // should not execute - $errors++; + ++$errors; }); $request = Request::create('/foo'); @@ -189,7 +190,7 @@ class ExceptionHandlerTest extends \PHPUnit_Framework_TestCase public function testNoResponseExceptionHandler() { $app = new Application(); - $app['exception_handler']->disable(); + unset($app['exception_handler']); $app->match('/foo', function () { throw new \RuntimeException('foo exception'); @@ -198,7 +199,7 @@ class ExceptionHandlerTest extends \PHPUnit_Framework_TestCase $errors = 0; $app->error(function ($e) use (&$errors) { - $errors++; + ++$errors; }); try { @@ -207,6 +208,8 @@ class ExceptionHandlerTest extends \PHPUnit_Framework_TestCase $this->fail('->handle() should not catch exceptions where an empty error handler was supplied'); } catch (\RuntimeException $e) { $this->assertEquals('foo exception', $e->getMessage()); + } catch (\LogicException $e) { + $this->assertEquals('foo exception', $e->getPrevious()->getMessage()); } $this->assertEquals(1, $errors, 'should execute the error handler'); @@ -261,7 +264,7 @@ class ExceptionHandlerTest extends \PHPUnit_Framework_TestCase // just making sure the dispatcher gets created }); - $app['exception_handler']->disable(); + unset($app['exception_handler']); try { $request = Request::create('/foo'); @@ -282,7 +285,7 @@ class ExceptionHandlerTest extends \PHPUnit_Framework_TestCase }); $app->error(function (\Exception $e) { - return new Response("Exception thrown", 500); + return new Response('Exception thrown', 500); }); $request = Request::create('/foo'); @@ -305,11 +308,10 @@ class ExceptionHandlerTest extends \PHPUnit_Framework_TestCase // Since we throw a standard Exception above only // the second error handler should fire $app->error(function (\LogicException $e) { // Extends \Exception - - return "Caught LogicException"; + return 'Caught LogicException'; }); $app->error(function (\Exception $e) { - return "Caught Exception"; + return 'Caught Exception'; }); $request = Request::create('/foo'); @@ -331,11 +333,10 @@ class ExceptionHandlerTest extends \PHPUnit_Framework_TestCase // Since we throw a LogicException above // the first error handler should fire $app->error(function (\LogicException $e) { // Extends \Exception - - return "Caught LogicException"; + return 'Caught LogicException'; }); $app->error(function (\Exception $e) { - return "Caught Exception"; + return 'Caught Exception'; }); $request = Request::create('/foo'); @@ -359,11 +360,10 @@ class ExceptionHandlerTest extends \PHPUnit_Framework_TestCase // the \Exception error handler is registered first and also // captures all exceptions that extend it $app->error(function (\Exception $e) { - return "Caught Exception"; + return 'Caught Exception'; }); $app->error(function (\LogicException $e) { // Extends \Exception - - return "Caught LogicException"; + return 'Caught LogicException'; }); $request = Request::create('/foo'); diff --git a/lib/silex/vendor/silex/silex/tests/Silex/Tests/Fixtures/Php7Controller.php b/lib/silex/vendor/silex/silex/tests/Silex/Tests/Fixtures/Php7Controller.php new file mode 100644 index 000000000..c16f14edb --- /dev/null +++ b/lib/silex/vendor/silex/silex/tests/Silex/Tests/Fixtures/Php7Controller.php @@ -0,0 +1,22 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace Silex\Tests\Fixtures; + +use Silex\Application; + +class Php7Controller +{ + public function typehintedAction(Application $application, string $name) + { + return 'Hello '.$application->escape($name).' in '.get_class($application); + } +} diff --git a/lib/silex/vendor/silex/silex/tests/Silex/Tests/Fixtures/manifest.json b/lib/silex/vendor/silex/silex/tests/Silex/Tests/Fixtures/manifest.json new file mode 100644 index 000000000..78c3bd61a --- /dev/null +++ b/lib/silex/vendor/silex/silex/tests/Silex/Tests/Fixtures/manifest.json @@ -0,0 +1,3 @@ +{ + "app.js": "some-random-hash.js" +} \ No newline at end of file diff --git a/lib/silex/vendor/silex/silex/tests/Silex/Tests/FunctionalTest.php b/lib/silex/vendor/silex/silex/tests/Silex/Tests/FunctionalTest.php index f2af4ac38..c6ee3359b 100644 --- a/lib/silex/vendor/silex/silex/tests/Silex/Tests/FunctionalTest.php +++ b/lib/silex/vendor/silex/silex/tests/Silex/Tests/FunctionalTest.php @@ -11,6 +11,7 @@ namespace Silex\Tests; +use PHPUnit\Framework\TestCase; use Silex\Application; use Silex\Route; use Silex\ControllerCollection; @@ -22,7 +23,7 @@ use Symfony\Component\HttpFoundation\Response; * * @author Igor Wiedler */ -class FunctionalTest extends \PHPUnit_Framework_TestCase +class FunctionalTest extends TestCase { public function testBind() { diff --git a/lib/silex/vendor/silex/silex/tests/Silex/Tests/JsonTest.php b/lib/silex/vendor/silex/silex/tests/Silex/Tests/JsonTest.php index 5eb133621..950437c37 100644 --- a/lib/silex/vendor/silex/silex/tests/Silex/Tests/JsonTest.php +++ b/lib/silex/vendor/silex/silex/tests/Silex/Tests/JsonTest.php @@ -11,6 +11,7 @@ namespace Silex\Tests; +use PHPUnit\Framework\TestCase; use Silex\Application; /** @@ -18,7 +19,7 @@ use Silex\Application; * * @author Igor Wiedler */ -class JsonTest extends \PHPUnit_Framework_TestCase +class JsonTest extends TestCase { public function testJsonReturnsJsonResponse() { diff --git a/lib/silex/vendor/silex/silex/tests/Silex/Tests/LazyDispatcherTest.php b/lib/silex/vendor/silex/silex/tests/Silex/Tests/LazyDispatcherTest.php index cfe690689..fcbbc62a9 100644 --- a/lib/silex/vendor/silex/silex/tests/Silex/Tests/LazyDispatcherTest.php +++ b/lib/silex/vendor/silex/silex/tests/Silex/Tests/LazyDispatcherTest.php @@ -11,10 +11,11 @@ namespace Silex\Tests; +use PHPUnit\Framework\TestCase; use Silex\Application; use Symfony\Component\HttpFoundation\Request; -class LazyDispatcherTest extends \PHPUnit_Framework_TestCase +class LazyDispatcherTest extends TestCase { /** @test */ public function beforeMiddlewareShouldNotCreateDispatcherEarly() @@ -22,11 +23,11 @@ class LazyDispatcherTest extends \PHPUnit_Framework_TestCase $dispatcherCreated = false; $app = new Application(); - $app['dispatcher'] = $app->share($app->extend('dispatcher', function ($dispatcher, $app) use (&$dispatcherCreated) { + $app->extend('dispatcher', function ($dispatcher, $app) use (&$dispatcherCreated) { $dispatcherCreated = true; return $dispatcher; - })); + }); $app->before(function () {}); @@ -44,7 +45,7 @@ class LazyDispatcherTest extends \PHPUnit_Framework_TestCase $app = new Application(); $fired = false; - $app->get("/", function () use ($app, &$fired) { + $app->get('/', function () use ($app, &$fired) { $app->finish(function () use (&$fired) { $fired = true; }); diff --git a/lib/silex/vendor/silex/silex/tests/Silex/Tests/LazyRequestMatcherTest.php b/lib/silex/vendor/silex/silex/tests/Silex/Tests/LazyRequestMatcherTest.php new file mode 100644 index 000000000..879d46fac --- /dev/null +++ b/lib/silex/vendor/silex/silex/tests/Silex/Tests/LazyRequestMatcherTest.php @@ -0,0 +1,78 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace Silex\Tests; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\HttpFoundation\Request; +use Silex\Provider\Routing\LazyRequestMatcher; + +/** + * LazyRequestMatcher test case. + * + * @author Leszek Prabucki + */ +class LazyRequestMatcherTest extends TestCase +{ + /** + * @covers \Silex\LazyRequestMatcher::getRequestMatcher + */ + public function testUserMatcherIsCreatedLazily() + { + $callCounter = 0; + $requestMatcher = $this->getMockBuilder('Symfony\Component\Routing\Matcher\RequestMatcherInterface')->getMock(); + + $matcher = new LazyRequestMatcher(function () use ($requestMatcher, &$callCounter) { + ++$callCounter; + + return $requestMatcher; + }); + + $this->assertEquals(0, $callCounter); + $request = Request::create('path'); + $matcher->matchRequest($request); + $this->assertEquals(1, $callCounter); + } + + /** + * @expectedException \LogicException + * @expectedExceptionMessage Factory supplied to LazyRequestMatcher must return implementation of Symfony\Component\Routing\RequestMatcherInterface. + */ + public function testThatCanInjectRequestMatcherOnly() + { + $matcher = new LazyRequestMatcher(function () { + return 'someMatcher'; + }); + + $request = Request::create('path'); + $matcher->matchRequest($request); + } + + /** + * @covers \Silex\LazyRequestMatcher::matchRequest + */ + public function testMatchIsProxy() + { + $request = Request::create('path'); + $matcher = $this->getMockBuilder('Symfony\Component\Routing\Matcher\RequestMatcherInterface')->getMock(); + $matcher->expects($this->once()) + ->method('matchRequest') + ->with($request) + ->will($this->returnValue('matcherReturnValue')); + + $matcher = new LazyRequestMatcher(function () use ($matcher) { + return $matcher; + }); + $result = $matcher->matchRequest($request); + + $this->assertEquals('matcherReturnValue', $result); + } +} diff --git a/lib/silex/vendor/silex/silex/tests/Silex/Tests/LocaleTest.php b/lib/silex/vendor/silex/silex/tests/Silex/Tests/LocaleTest.php index 483ea2647..81bf04227 100644 --- a/lib/silex/vendor/silex/silex/tests/Silex/Tests/LocaleTest.php +++ b/lib/silex/vendor/silex/silex/tests/Silex/Tests/LocaleTest.php @@ -11,7 +11,9 @@ namespace Silex\Tests; +use PHPUnit\Framework\TestCase; use Silex\Application; +use Silex\Provider\LocaleServiceProvider; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpKernel\HttpKernelInterface; @@ -20,22 +22,25 @@ use Symfony\Component\HttpKernel\HttpKernelInterface; * * @author Fabien Potencier */ -class LocaleTest extends \PHPUnit_Framework_TestCase +class LocaleTest extends TestCase { public function testLocale() { $app = new Application(); + $app->register(new LocaleServiceProvider()); $app->get('/', function (Request $request) { return $request->getLocale(); }); $response = $app->handle(Request::create('/')); $this->assertEquals('en', $response->getContent()); $app = new Application(); + $app->register(new LocaleServiceProvider()); $app['locale'] = 'fr'; $app->get('/', function (Request $request) { return $request->getLocale(); }); $response = $app->handle(Request::create('/')); $this->assertEquals('fr', $response->getContent()); $app = new Application(); + $app->register(new LocaleServiceProvider()); $app->get('/{_locale}', function (Request $request) { return $request->getLocale(); }); $response = $app->handle(Request::create('/es')); $this->assertEquals('es', $response->getContent()); @@ -44,6 +49,7 @@ class LocaleTest extends \PHPUnit_Framework_TestCase public function testLocaleInSubRequests() { $app = new Application(); + $app->register(new LocaleServiceProvider()); $app->get('/embed/{_locale}', function (Request $request) { return $request->getLocale(); }); $app->get('/{_locale}', function (Request $request) use ($app) { return $request->getLocale().$app->handle(Request::create('/embed/es'), HttpKernelInterface::SUB_REQUEST)->getContent().$request->getLocale(); @@ -52,6 +58,7 @@ class LocaleTest extends \PHPUnit_Framework_TestCase $this->assertEquals('fresfr', $response->getContent()); $app = new Application(); + $app->register(new LocaleServiceProvider()); $app->get('/embed', function (Request $request) { return $request->getLocale(); }); $app->get('/{_locale}', function (Request $request) use ($app) { return $request->getLocale().$app->handle(Request::create('/embed'), HttpKernelInterface::SUB_REQUEST)->getContent().$request->getLocale(); @@ -64,6 +71,7 @@ class LocaleTest extends \PHPUnit_Framework_TestCase public function testLocaleWithBefore() { $app = new Application(); + $app->register(new LocaleServiceProvider()); $app->before(function (Request $request) use ($app) { $request->setLocale('fr'); }); $app->get('/embed', function (Request $request) { return $request->getLocale(); }); $app->get('/', function (Request $request) use ($app) { diff --git a/lib/silex/vendor/silex/silex/tests/Silex/Tests/MiddlewareTest.php b/lib/silex/vendor/silex/silex/tests/Silex/Tests/MiddlewareTest.php index b2c073b92..6335dad3c 100644 --- a/lib/silex/vendor/silex/silex/tests/Silex/Tests/MiddlewareTest.php +++ b/lib/silex/vendor/silex/silex/tests/Silex/Tests/MiddlewareTest.php @@ -11,6 +11,7 @@ namespace Silex\Tests; +use PHPUnit\Framework\TestCase; use Silex\Application; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; @@ -20,7 +21,7 @@ use Symfony\Component\HttpFoundation\Response; * * @author Igor Wiedler */ -class MiddlewareTest extends \PHPUnit_Framework_TestCase +class MiddlewareTest extends TestCase { public function testBeforeAndAfterFilter() { @@ -31,17 +32,17 @@ class MiddlewareTest extends \PHPUnit_Framework_TestCase $app->before(function () use (&$i, $test) { $test->assertEquals(0, $i); - $i++; + ++$i; }); $app->match('/foo', function () use (&$i, $test) { $test->assertEquals(1, $i); - $i++; + ++$i; }); $app->after(function () use (&$i, $test) { $test->assertEquals(2, $i); - $i++; + ++$i; }); $request = Request::create('/foo'); @@ -57,13 +58,13 @@ class MiddlewareTest extends \PHPUnit_Framework_TestCase $app = new Application(); $app->match('/foo', function () use (&$i) { - $i++; + ++$i; return new Response('foo'); }); $app->after(function () use (&$i) { - $i++; + ++$i; }); $request = Request::create('/foo'); @@ -81,27 +82,27 @@ class MiddlewareTest extends \PHPUnit_Framework_TestCase $app->before(function () use (&$i, $test) { $test->assertEquals(0, $i); - $i++; + ++$i; }); $app->before(function () use (&$i, $test) { $test->assertEquals(1, $i); - $i++; + ++$i; }); $app->match('/foo', function () use (&$i, $test) { $test->assertEquals(2, $i); - $i++; + ++$i; }); $app->after(function () use (&$i, $test) { $test->assertEquals(3, $i); - $i++; + ++$i; }); $app->after(function () use (&$i, $test) { $test->assertEquals(4, $i); - $i++; + ++$i; }); $request = Request::create('/foo'); @@ -117,7 +118,7 @@ class MiddlewareTest extends \PHPUnit_Framework_TestCase $app = new Application(); $app->before(function () use (&$i) { - $i++; + ++$i; }); $app->match('/foo', function () { @@ -125,7 +126,7 @@ class MiddlewareTest extends \PHPUnit_Framework_TestCase }); $app->after(function () use (&$i) { - $i++; + ++$i; }); $app->error(function () { @@ -145,11 +146,11 @@ class MiddlewareTest extends \PHPUnit_Framework_TestCase $app = new Application(); $app->before(function () use (&$i) { - $i++; + ++$i; }, Application::EARLY_EVENT); $app->after(function () use (&$i) { - $i++; + ++$i; }); $app->error(function () { @@ -191,8 +192,8 @@ class MiddlewareTest extends \PHPUnit_Framework_TestCase { $app = new Application(); - $app->before(function () use ($app) { - $app['project'] = $app['request']->get('project'); + $app->before(function (Request $request) use ($app) { + $app['project'] = $request->get('project'); }); $app->match('/foo/{project}', function () use ($app) { diff --git a/lib/silex/vendor/silex/silex/tests/Silex/Tests/Provider/AssetServiceProviderTest.php b/lib/silex/vendor/silex/silex/tests/Silex/Tests/Provider/AssetServiceProviderTest.php new file mode 100644 index 000000000..940fdca3d --- /dev/null +++ b/lib/silex/vendor/silex/silex/tests/Silex/Tests/Provider/AssetServiceProviderTest.php @@ -0,0 +1,52 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace Silex\Tests\Provider; + +use PHPUnit\Framework\TestCase; +use Silex\Application; +use Silex\Provider\AssetServiceProvider; + +class AssetServiceProviderTest extends TestCase +{ + public function testGenerateAssetUrl() + { + $app = new Application(); + $app->register(new AssetServiceProvider(), array( + 'assets.version' => 'v1', + 'assets.version_format' => '%s?version=%s', + 'assets.named_packages' => array( + 'css' => array('version' => 'css2', 'base_path' => '/whatever-makes-sense'), + 'images' => array('base_urls' => array('https://img.example.com')), + ), + )); + + $this->assertEquals('/foo.png?version=v1', $app['assets.packages']->getUrl('/foo.png')); + $this->assertEquals('/whatever-makes-sense/foo.css?css2', $app['assets.packages']->getUrl('foo.css', 'css')); + $this->assertEquals('https://img.example.com/foo.png', $app['assets.packages']->getUrl('/foo.png', 'images')); + } + + public function testJsonManifestVersionStrategy() + { + if (!class_exists('Symfony\Component\Asset\VersionStrategy\JsonManifestVersionStrategy')) { + $this->markTestSkipped('JsonManifestVersionStrategy class is not available.'); + + return; + } + + $app = new Application(); + $app->register(new AssetServiceProvider(), array( + 'assets.json_manifest_path' => __DIR__.'/../Fixtures/manifest.json', + )); + + $this->assertEquals('/some-random-hash.js', $app['assets.packages']->getUrl('app.js')); + } +} diff --git a/lib/silex/vendor/silex/silex/tests/Silex/Tests/Provider/DoctrineServiceProviderTest.php b/lib/silex/vendor/silex/silex/tests/Silex/Tests/Provider/DoctrineServiceProviderTest.php index e6e1d7538..504e0ef3c 100644 --- a/lib/silex/vendor/silex/silex/tests/Silex/Tests/Provider/DoctrineServiceProviderTest.php +++ b/lib/silex/vendor/silex/silex/tests/Silex/Tests/Provider/DoctrineServiceProviderTest.php @@ -11,6 +11,8 @@ namespace Silex\Tests\Provider; +use PHPUnit\Framework\TestCase; +use Pimple\Container; use Silex\Application; use Silex\Provider\DoctrineServiceProvider; @@ -19,7 +21,7 @@ use Silex\Provider\DoctrineServiceProvider; * * Fabien Potencier */ -class DoctrineServiceProviderTest extends \PHPUnit_Framework_TestCase +class DoctrineServiceProviderTest extends TestCase { public function testOptionsInitializer() { @@ -42,7 +44,7 @@ class DoctrineServiceProviderTest extends \PHPUnit_Framework_TestCase $db = $app['db']; $params = $db->getParams(); - $this->assertTrue(array_key_exists('memory', $params)); + $this->assertArrayHasKey('memory', $params); $this->assertTrue($params['memory']); $this->assertInstanceof('Doctrine\DBAL\Driver\PDOSqlite\Driver', $db->getDriver()); $this->assertEquals(22, $app['db']->fetchColumn('SELECT 22')); @@ -66,7 +68,7 @@ class DoctrineServiceProviderTest extends \PHPUnit_Framework_TestCase $db = $app['db']; $params = $db->getParams(); - $this->assertTrue(array_key_exists('memory', $params)); + $this->assertArrayHasKey('memory', $params); $this->assertTrue($params['memory']); $this->assertInstanceof('Doctrine\DBAL\Driver\PDOSqlite\Driver', $db->getDriver()); $this->assertEquals(22, $app['db']->fetchColumn('SELECT 22')); @@ -75,7 +77,41 @@ class DoctrineServiceProviderTest extends \PHPUnit_Framework_TestCase $db2 = $app['dbs']['sqlite2']; $params = $db2->getParams(); - $this->assertTrue(array_key_exists('path', $params)); + $this->assertArrayHasKey('path', $params); $this->assertEquals(sys_get_temp_dir().'/silex', $params['path']); } + + public function testLoggerLoading() + { + if (!in_array('sqlite', \PDO::getAvailableDrivers())) { + $this->markTestSkipped('pdo_sqlite is not available'); + } + + $app = new Application(); + $this->assertArrayHasKey('logger', $app); + $this->assertNull($app['logger']); + $app->register(new DoctrineServiceProvider(), array( + 'dbs.options' => array( + 'sqlite1' => array('driver' => 'pdo_sqlite', 'memory' => true), + ), + )); + $this->assertEquals(22, $app['db']->fetchColumn('SELECT 22')); + $this->assertNull($app['db']->getConfiguration()->getSQLLogger()); + } + + public function testLoggerNotLoaded() + { + if (!in_array('sqlite', \PDO::getAvailableDrivers())) { + $this->markTestSkipped('pdo_sqlite is not available'); + } + + $app = new Container(); + $app->register(new DoctrineServiceProvider(), array( + 'dbs.options' => array( + 'sqlite1' => array('driver' => 'pdo_sqlite', 'memory' => true), + ), + )); + $this->assertEquals(22, $app['db']->fetchColumn('SELECT 22')); + $this->assertNull($app['db']->getConfiguration()->getSQLLogger()); + } } diff --git a/lib/silex/vendor/silex/silex/tests/Silex/Tests/Provider/FormServiceProviderTest.php b/lib/silex/vendor/silex/silex/tests/Silex/Tests/Provider/FormServiceProviderTest.php index ec68013da..981826e6f 100644 --- a/lib/silex/vendor/silex/silex/tests/Silex/Tests/Provider/FormServiceProviderTest.php +++ b/lib/silex/vendor/silex/silex/tests/Silex/Tests/Provider/FormServiceProviderTest.php @@ -11,19 +11,22 @@ namespace Silex\Tests\Provider; +use PHPUnit\Framework\TestCase; use Silex\Application; use Silex\Provider\FormServiceProvider; +use Silex\Provider\CsrfServiceProvider; +use Silex\Provider\SessionServiceProvider; use Silex\Provider\TranslationServiceProvider; use Silex\Provider\ValidatorServiceProvider; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\AbstractTypeExtension; -use Symfony\Component\Form\Extension\Csrf\CsrfProvider\CsrfProviderInterface; use Symfony\Component\Form\FormTypeGuesserChain; use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\OptionsResolver\OptionsResolver; use Symfony\Component\OptionsResolver\OptionsResolverInterface; use Symfony\Component\Translation\Exception\NotFoundResourceException; -class FormServiceProviderTest extends \PHPUnit_Framework_TestCase +class FormServiceProviderTest extends TestCase { public function testFormFactoryServiceIsFormFactory() { @@ -32,59 +35,193 @@ class FormServiceProviderTest extends \PHPUnit_Framework_TestCase $this->assertInstanceOf('Symfony\Component\Form\FormFactory', $app['form.factory']); } + public function testFormRegistryServiceIsFormRegistry() + { + $app = new Application(); + $app->register(new FormServiceProvider()); + $this->assertInstanceOf('Symfony\Component\Form\FormRegistry', $app['form.registry']); + } + public function testFormServiceProviderWillLoadTypes() { $app = new Application(); $app->register(new FormServiceProvider()); - $app['form.types'] = $app->share($app->extend('form.types', function ($extensions) { + $app->extend('form.types', function ($extensions) { $extensions[] = new DummyFormType(); return $extensions; - })); + }); - $form = $app['form.factory']->createBuilder('form', array()) + $form = $app['form.factory']->createBuilder('Symfony\Component\Form\Extension\Core\Type\FormType', array()) + ->add('dummy', 'Silex\Tests\Provider\DummyFormType') + ->getForm(); + + $this->assertInstanceOf('Symfony\Component\Form\Form', $form); + } + + public function testFormServiceProviderWillLoadTypesServices() + { + $app = new Application(); + + $app->register(new FormServiceProvider()); + + $app['dummy'] = function () { + return new DummyFormType(); + }; + $app->extend('form.types', function ($extensions) { + $extensions[] = 'dummy'; + + return $extensions; + }); + + $form = $app['form.factory'] + ->createBuilder('Symfony\Component\Form\Extension\Core\Type\FormType', array()) ->add('dummy', 'dummy') ->getForm(); $this->assertInstanceOf('Symfony\Component\Form\Form', $form); } + /** + * @expectedException \Symfony\Component\Form\Exception\InvalidArgumentException + * @expectedExceptionMessage Invalid form type. The silex service "dummy" does not exist. + */ + public function testNonExistentTypeService() + { + $app = new Application(); + + $app->register(new FormServiceProvider()); + + $app->extend('form.types', function ($extensions) { + $extensions[] = 'dummy'; + + return $extensions; + }); + + $app['form.factory'] + ->createBuilder('Symfony\Component\Form\Extension\Core\Type\FormType', array()) + ->add('dummy', 'dummy') + ->getForm(); + } + public function testFormServiceProviderWillLoadTypeExtensions() { $app = new Application(); $app->register(new FormServiceProvider()); - $app['form.type.extensions'] = $app->share($app->extend('form.type.extensions', function ($extensions) { + $app->extend('form.type.extensions', function ($extensions) { $extensions[] = new DummyFormTypeExtension(); return $extensions; - })); + }); - $form = $app['form.factory']->createBuilder('form', array()) - ->add('file', 'file', array('image_path' => 'webPath')) + $form = $app['form.factory']->createBuilder('Symfony\Component\Form\Extension\Core\Type\FormType', array()) + ->add('file', 'Symfony\Component\Form\Extension\Core\Type\FileType', array('image_path' => 'webPath')) ->getForm(); $this->assertInstanceOf('Symfony\Component\Form\Form', $form); } + public function testFormServiceProviderWillLoadTypeExtensionsServices() + { + $app = new Application(); + + $app->register(new FormServiceProvider()); + + $app['dummy.form.type.extension'] = function () { + return new DummyFormTypeExtension(); + }; + $app->extend('form.type.extensions', function ($extensions) { + $extensions[] = 'dummy.form.type.extension'; + + return $extensions; + }); + + $form = $app['form.factory'] + ->createBuilder('Symfony\Component\Form\Extension\Core\Type\FormType', array()) + ->add('file', 'Symfony\Component\Form\Extension\Core\Type\FileType', array('image_path' => 'webPath')) + ->getForm(); + + $this->assertInstanceOf('Symfony\Component\Form\Form', $form); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\InvalidArgumentException + * @expectedExceptionMessage Invalid form type extension. The silex service "dummy.form.type.extension" does not exist. + */ + public function testNonExistentTypeExtensionService() + { + $app = new Application(); + + $app->register(new FormServiceProvider()); + + $app->extend('form.type.extensions', function ($extensions) { + $extensions[] = 'dummy.form.type.extension'; + + return $extensions; + }); + + $app['form.factory'] + ->createBuilder('Symfony\Component\Form\Extension\Core\Type\FormType', array()) + ->add('dummy', 'dummy.form.type') + ->getForm(); + } + public function testFormServiceProviderWillLoadTypeGuessers() { $app = new Application(); $app->register(new FormServiceProvider()); - $app['form.type.guessers'] = $app->share($app->extend('form.type.guessers', function ($guessers) { + $app->extend('form.type.guessers', function ($guessers) { $guessers[] = new FormTypeGuesserChain(array()); return $guessers; - })); + }); $this->assertInstanceOf('Symfony\Component\Form\FormFactory', $app['form.factory']); } + public function testFormServiceProviderWillLoadTypeGuessersServices() + { + $app = new Application(); + + $app->register(new FormServiceProvider()); + + $app['dummy.form.type.guesser'] = function () { + return new FormTypeGuesserChain(array()); + }; + $app->extend('form.type.guessers', function ($guessers) { + $guessers[] = 'dummy.form.type.guesser'; + + return $guessers; + }); + + $this->assertInstanceOf('Symfony\Component\Form\FormFactory', $app['form.factory']); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\InvalidArgumentException + * @expectedExceptionMessage Invalid form type guesser. The silex service "dummy.form.type.guesser" does not exist. + */ + public function testNonExistentTypeGuesserService() + { + $app = new Application(); + + $app->register(new FormServiceProvider()); + + $app->extend('form.type.guessers', function ($extensions) { + $extensions[] = 'dummy.form.type.guesser'; + + return $extensions; + }); + + $factory = $app['form.factory']; + } + public function testFormServiceProviderWillUseTranslatorIfAvailable() { $app = new Application(); @@ -100,11 +237,11 @@ class FormServiceProviderTest extends \PHPUnit_Framework_TestCase ); $app['locale'] = 'de'; - $app['form.csrf_provider'] = $app->share(function () { - return new FakeCsrfProvider(); - }); + $app['csrf.token_manager'] = function () { + return $this->getMockBuilder('Symfony\Component\Security\Csrf\CsrfTokenManagerInterface')->getMock(); + }; - $form = $app['form.factory']->createBuilder('form', array()) + $form = $app['form.factory']->createBuilder('Symfony\Component\Form\Extension\Core\Type\FormType', array()) ->getForm(); $form->handleRequest($req = Request::create('/', 'POST', array('form' => array( @@ -112,7 +249,13 @@ class FormServiceProviderTest extends \PHPUnit_Framework_TestCase )))); $this->assertFalse($form->isValid()); - $this->assertContains('ERROR: German translation', $form->getErrorsAsString()); + $r = new \ReflectionMethod($form, 'getErrors'); + if (!$r->getNumberOfParameters()) { + $this->assertContains('ERROR: German translation', $form->getErrorsAsString()); + } else { + // as of 2.5 + $this->assertContains('ERROR: German translation', (string) $form->getErrors(true, false)); + } } public function testFormServiceProviderWillNotAddNonexistentTranslationFiles() @@ -136,41 +279,85 @@ class FormServiceProviderTest extends \PHPUnit_Framework_TestCase $this->fail('Form factory should not add a translation resource that does not exist'); } } -} -class DummyFormType extends AbstractType -{ - /** - * @return string The name of this type - */ - public function getName() + public function testFormCsrf() { - return 'dummy'; + $app = new Application(); + $app->register(new FormServiceProvider()); + $app->register(new SessionServiceProvider()); + $app->register(new CsrfServiceProvider()); + $app['session.test'] = true; + + $form = $app['form.factory']->createBuilder('Symfony\Component\Form\Extension\Core\Type\FormType', array())->getForm(); + + $this->assertTrue(isset($form->createView()['_token'])); + } + + public function testUserExtensionCanConfigureDefaultExtensions() + { + $app = new Application(); + $app->register(new FormServiceProvider()); + $app->register(new SessionServiceProvider()); + $app->register(new CsrfServiceProvider()); + $app['session.test'] = true; + + $app->extend('form.type.extensions', function ($extensions) { + $extensions[] = new FormServiceProviderTest\DisableCsrfExtension(); + + return $extensions; + }); + $form = $app['form.factory']->createBuilder('Symfony\Component\Form\Extension\Core\Type\FormType', array())->getForm(); + + $this->assertFalse($form->getConfig()->getOption('csrf_protection')); } } -class DummyFormTypeExtension extends AbstractTypeExtension -{ - public function getExtendedType() +if (!class_exists('Symfony\Component\Form\Deprecated\FormEvents')) { + class DummyFormType extends AbstractType { - return 'file'; } - - public function setDefaultOptions(OptionsResolverInterface $resolver) +} else { + // FormTypeInterface::getName() is needed by the form component 2.8.x + class DummyFormType extends AbstractType { - $resolver->setOptional(array('image_path')); + /** + * @return string The name of this type + */ + public function getName() + { + return 'dummy'; + } } } -class FakeCsrfProvider implements CsrfProviderInterface -{ - public function generateCsrfToken($intention) +if (method_exists('Symfony\Component\Form\AbstractType', 'configureOptions')) { + class DummyFormTypeExtension extends AbstractTypeExtension { - return $intention.'123'; - } + public function getExtendedType() + { + return 'Symfony\Component\Form\Extension\Core\Type\FileType'; + } - public function isCsrfTokenValid($intention, $token) + public function configureOptions(OptionsResolver $resolver) + { + $resolver->setDefined(array('image_path')); + } + } +} else { + class DummyFormTypeExtension extends AbstractTypeExtension { - return $token === $this->generateCsrfToken($intention); + public function getExtendedType() + { + return 'Symfony\Component\Form\Extension\Core\Type\FileType'; + } + + public function setDefaultOptions(OptionsResolverInterface $resolver) + { + if (!method_exists($resolver, 'setDefined')) { + $resolver->setOptional(array('image_path')); + } else { + $resolver->setDefined(array('image_path')); + } + } } } diff --git a/lib/silex/vendor/silex/silex/tests/Silex/Tests/Provider/FormServiceProviderTest/DisableCsrfExtension.php b/lib/silex/vendor/silex/silex/tests/Silex/Tests/Provider/FormServiceProviderTest/DisableCsrfExtension.php new file mode 100644 index 000000000..8c82237db --- /dev/null +++ b/lib/silex/vendor/silex/silex/tests/Silex/Tests/Provider/FormServiceProviderTest/DisableCsrfExtension.php @@ -0,0 +1,22 @@ +setDefaults(array( + 'csrf_protection' => false, + )); + } + + public function getExtendedType() + { + return FormType::class; + } +} diff --git a/lib/silex/vendor/silex/silex/tests/Silex/Tests/Provider/HttpCacheServiceProviderTest.php b/lib/silex/vendor/silex/silex/tests/Silex/Tests/Provider/HttpCacheServiceProviderTest.php index 6873df2b4..dca3f6a84 100644 --- a/lib/silex/vendor/silex/silex/tests/Silex/Tests/Provider/HttpCacheServiceProviderTest.php +++ b/lib/silex/vendor/silex/silex/tests/Silex/Tests/Provider/HttpCacheServiceProviderTest.php @@ -11,6 +11,7 @@ namespace Silex\Tests\Provider; +use PHPUnit\Framework\TestCase; use Silex\Application; use Silex\Provider\HttpCacheServiceProvider; use Symfony\Component\HttpFoundation\Request; @@ -21,7 +22,7 @@ use Symfony\Component\HttpFoundation\Response; * * @author Igor Wiedler */ -class HttpCacheServiceProviderTest extends \PHPUnit_Framework_TestCase +class HttpCacheServiceProviderTest extends TestCase { public function testRegister() { @@ -31,7 +32,7 @@ class HttpCacheServiceProviderTest extends \PHPUnit_Framework_TestCase 'http_cache.cache_dir' => sys_get_temp_dir().'/silex_http_cache_'.uniqid(), )); - $this->assertInstanceOf('Silex\HttpCache', $app['http_cache']); + $this->assertInstanceOf('Silex\Provider\HttpCache\HttpCache', $app['http_cache']); return $app; } diff --git a/lib/silex/vendor/silex/silex/tests/Silex/Tests/Provider/HttpFragmentServiceProviderTest.php b/lib/silex/vendor/silex/silex/tests/Silex/Tests/Provider/HttpFragmentServiceProviderTest.php index fec52267d..6e12fcc5d 100644 --- a/lib/silex/vendor/silex/silex/tests/Silex/Tests/Provider/HttpFragmentServiceProviderTest.php +++ b/lib/silex/vendor/silex/silex/tests/Silex/Tests/Provider/HttpFragmentServiceProviderTest.php @@ -11,28 +11,26 @@ namespace Silex\Tests\Provider; +use PHPUnit\Framework\TestCase; use Silex\Application; use Silex\Provider\HttpCacheServiceProvider; use Silex\Provider\HttpFragmentServiceProvider; use Silex\Provider\TwigServiceProvider; use Symfony\Component\HttpFoundation\Request; -class HttpFragmentServiceProviderTest extends \PHPUnit_Framework_TestCase +class HttpFragmentServiceProviderTest extends TestCase { public function testRenderFunction() { - if (!class_exists('Symfony\Component\HttpFoundation\RequestStack')) { - $this->markTestSkipped('HttpFragmentServiceProvider is not available on Symfony <2.4'); - } - $app = new Application(); + unset($app['exception_handler']); $app->register(new HttpFragmentServiceProvider()); $app->register(new HttpCacheServiceProvider(), array('http_cache.cache_dir' => sys_get_temp_dir())); $app->register(new TwigServiceProvider(), array( 'twig.templates' => array( 'hello' => '{{ render("/foo") }}{{ render_esi("/foo") }}{{ render_hinclude("/foo") }}', - 'foo' => 'foo', + 'foo' => 'foo', ), )); diff --git a/lib/silex/vendor/silex/silex/tests/Silex/Tests/Provider/MonologServiceProviderTest.php b/lib/silex/vendor/silex/silex/tests/Silex/Tests/Provider/MonologServiceProviderTest.php index c269690be..4585378f0 100644 --- a/lib/silex/vendor/silex/silex/tests/Silex/Tests/Provider/MonologServiceProviderTest.php +++ b/lib/silex/vendor/silex/silex/tests/Silex/Tests/Provider/MonologServiceProviderTest.php @@ -11,20 +11,37 @@ namespace Silex\Tests\Provider; +use Monolog\Formatter\JsonFormatter; use Monolog\Handler\TestHandler; use Monolog\Logger; +use PHPUnit\Framework\TestCase; use Silex\Application; use Silex\Provider\MonologServiceProvider; use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; +use Symfony\Component\HttpKernel\Kernel; /** * MonologProvider test cases. * * @author Igor Wiedler */ -class MonologServiceProviderTest extends \PHPUnit_Framework_TestCase +class MonologServiceProviderTest extends TestCase { + private $currErrorHandler; + + protected function setUp() + { + $this->currErrorHandler = set_error_handler('var_dump'); + restore_error_handler(); + } + + protected function tearDown() + { + set_error_handler($this->currErrorHandler); + } + public function testRequestLogging() { $app = $this->getApplication(); @@ -38,9 +55,16 @@ class MonologServiceProviderTest extends \PHPUnit_Framework_TestCase $request = Request::create('/foo'); $app->handle($request); - $this->assertTrue($app['monolog.handler']->hasInfo('> GET /foo')); - $this->assertTrue($app['monolog.handler']->hasInfo('< 200')); - $this->assertTrue($app['monolog.handler']->hasInfo('Matched route "GET_foo" (parameters: "_controller": "{}", "_route": "GET_foo")')); + $this->assertTrue($app['monolog.handler']->hasDebug('> GET /foo')); + $this->assertTrue($app['monolog.handler']->hasDebug('< 200')); + + $records = $app['monolog.handler']->getRecords(); + if (Kernel::VERSION_ID < 30100) { + $this->assertContains('Matched route "GET_foo"', $records[0]['message']); + } else { + $this->assertContains('Matched route "{route}".', $records[0]['message']); + $this->assertSame('GET_foo', $records[0]['context']['route']); + } } public function testManualLogging() @@ -59,6 +83,18 @@ class MonologServiceProviderTest extends \PHPUnit_Framework_TestCase $this->assertTrue($app['monolog.handler']->hasDebug('logging a message')); } + public function testOverrideFormatter() + { + $app = new Application(); + + $app->register(new MonologServiceProvider(), array( + 'monolog.formatter' => new JsonFormatter(), + 'monolog.logfile' => 'php://memory', + )); + + $this->assertInstanceOf('Monolog\Formatter\JsonFormatter', $app['monolog.handler']->getFormatter()); + } + public function testErrorLogging() { $app = $this->getApplication(); @@ -75,7 +111,6 @@ class MonologServiceProviderTest extends \PHPUnit_Framework_TestCase $request = Request::create('/error'); $app->handle($request); - $records = $app['monolog.handler']->getRecords(); $pattern = "#Symfony\\\\Component\\\\HttpKernel\\\\Exception\\\\NotFoundHttpException: No route found for \"GET /error\" \(uncaught exception\) at .* line \d+#"; $this->assertMatchingRecord($pattern, Logger::ERROR, $app['monolog.handler']); @@ -108,7 +143,7 @@ class MonologServiceProviderTest extends \PHPUnit_Framework_TestCase $request = Request::create('/foo'); $app->handle($request); - $this->assertTrue($app['monolog.handler']->hasInfo('< 302 /bar')); + $this->assertTrue($app['monolog.handler']->hasDebug('< 302 /bar')); } public function testErrorLoggingGivesWayToSecurityExceptionHandling() @@ -166,7 +201,32 @@ class MonologServiceProviderTest extends \PHPUnit_Framework_TestCase $this->assertEmpty($app['monolog.handler']->getRecords(), 'Expected no logging to occur'); } - protected function assertMatchingRecord($pattern, $level, $handler) + public function testExceptionFiltering() + { + $app = new Application(); + $app->get('/foo', function () use ($app) { + throw new NotFoundHttpException(); + }); + + $level = Logger::ERROR; + $app->register(new MonologServiceProvider(), array( + 'monolog.exception.logger_filter' => $app->protect(function () { + return Logger::DEBUG; + }), + 'monolog.handler' => function () use ($app) { + return new TestHandler($app['monolog.level']); + }, + 'monolog.level' => $level, + 'monolog.logfile' => 'php://memory', + )); + + $request = Request::create('/foo'); + $app->handle($request); + + $this->assertCount(0, $app['monolog.handler']->getRecords(), 'Expected no logging to occur'); + } + + protected function assertMatchingRecord($pattern, $level, TestHandler $handler) { $found = false; $records = $handler->getRecords(); @@ -183,13 +243,14 @@ class MonologServiceProviderTest extends \PHPUnit_Framework_TestCase { $app = new Application(); - $app->register(new MonologServiceProvider()); + $app->register(new MonologServiceProvider(), array( + 'monolog.handler' => function () use ($app) { + $level = MonologServiceProvider::translateLevel($app['monolog.level']); - $app['monolog.handler'] = $app->share(function () use ($app) { - $level = MonologServiceProvider::translateLevel($app['monolog.level']); - - return new TestHandler($level); - }); + return new TestHandler($level); + }, + 'monolog.logfile' => 'php://memory', + )); return $app; } diff --git a/lib/silex/vendor/silex/silex/tests/Silex/Tests/Provider/RememberMeServiceProviderTest.php b/lib/silex/vendor/silex/silex/tests/Silex/Tests/Provider/RememberMeServiceProviderTest.php index d9bb33351..b027497cd 100644 --- a/lib/silex/vendor/silex/silex/tests/Silex/Tests/Provider/RememberMeServiceProviderTest.php +++ b/lib/silex/vendor/silex/silex/tests/Silex/Tests/Provider/RememberMeServiceProviderTest.php @@ -17,9 +17,10 @@ use Silex\Provider\RememberMeServiceProvider; use Silex\Provider\SecurityServiceProvider; use Silex\Provider\SessionServiceProvider; use Symfony\Component\HttpKernel\Client; +use Symfony\Component\Security\Http\SecurityEvents; /** - * SecurityServiceProvider + * SecurityServiceProvider. * * @author Fabien Potencier */ @@ -29,19 +30,17 @@ class RememberMeServiceProviderTest extends WebTestCase { $app = $this->createApplication(); - $event = false; - $app->on(\Symfony\Component\Security\Http\SecurityEvents::INTERACTIVE_LOGIN, function ($event) use ($app, &$event) { - $event = true; - }); + $interactiveLogin = new InteractiveLoginTriggered(); + $app->on(SecurityEvents::INTERACTIVE_LOGIN, array($interactiveLogin, 'onInteractiveLogin')); $client = new Client($app); $client->request('get', '/'); - $this->assertFalse($event, 'The interactive login has not been triggered yet'); + $this->assertFalse($interactiveLogin->triggered, 'The interactive login has not been triggered yet'); $client->request('post', '/login_check', array('_username' => 'fabien', '_password' => 'foo', '_remember_me' => 'true')); $client->followRedirect(); $this->assertEquals('AUTHENTICATED_FULLY', $client->getResponse()->getContent()); - $this->assertTrue($event, 'The interactive login has been triggered'); + $this->assertTrue($interactiveLogin->triggered, 'The interactive login has been triggered'); $this->assertNotNull($client->getCookiejar()->get('REMEMBERME'), 'The REMEMBERME cookie is set'); $event = false; @@ -50,7 +49,7 @@ class RememberMeServiceProviderTest extends WebTestCase $client->request('get', '/'); $this->assertEquals('AUTHENTICATED_REMEMBERED', $client->getResponse()->getContent()); - $this->assertTrue($event, 'The interactive login has been triggered'); + $this->assertTrue($interactiveLogin->triggered, 'The interactive login has been triggered'); $client->request('get', '/logout'); $client->followRedirect(); @@ -63,7 +62,7 @@ class RememberMeServiceProviderTest extends WebTestCase $app = new Application(); $app['debug'] = true; - $app['exception_handler']->disable(); + unset($app['exception_handler']); $app->register(new SessionServiceProvider(), array( 'session.test' => true, @@ -78,15 +77,15 @@ class RememberMeServiceProviderTest extends WebTestCase 'remember_me' => array(), 'logout' => true, 'users' => array( - 'fabien' => array('ROLE_USER', '5FZ2Z8QIkA7UTZ4BYkoC+GsReLf569mSKDsfods6LYQ8t+a8EW9oaircfMpmaLbPBh4FOBiiFyLfuZmTSUwzZg=='), + 'fabien' => array('ROLE_USER', '$2y$15$lzUNsTegNXvZW3qtfucV0erYBcEqWVeyOmjolB7R1uodsAVJ95vvu'), ), ), ); $app->get('/', function () use ($app) { - if ($app['security']->isGranted('IS_AUTHENTICATED_FULLY')) { + if ($app['security.authorization_checker']->isGranted('IS_AUTHENTICATED_FULLY')) { return 'AUTHENTICATED_FULLY'; - } elseif ($app['security']->isGranted('IS_AUTHENTICATED_REMEMBERED')) { + } elseif ($app['security.authorization_checker']->isGranted('IS_AUTHENTICATED_REMEMBERED')) { return 'AUTHENTICATED_REMEMBERED'; } else { return 'AUTHENTICATED_ANONYMOUSLY'; @@ -96,3 +95,13 @@ class RememberMeServiceProviderTest extends WebTestCase return $app; } } + +class InteractiveLoginTriggered +{ + public $triggered = false; + + public function onInteractiveLogin() + { + $this->triggered = true; + } +} diff --git a/lib/silex/vendor/silex/silex/tests/Silex/Tests/Provider/RoutingServiceProviderTest.php b/lib/silex/vendor/silex/silex/tests/Silex/Tests/Provider/RoutingServiceProviderTest.php new file mode 100644 index 000000000..2faaa683c --- /dev/null +++ b/lib/silex/vendor/silex/silex/tests/Silex/Tests/Provider/RoutingServiceProviderTest.php @@ -0,0 +1,122 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace Silex\Tests\Provider; + +use PHPUnit\Framework\TestCase; +use Pimple\Container; +use Silex\Application; +use Silex\Provider\RoutingServiceProvider; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\Routing\Generator\UrlGeneratorInterface; + +/** + * RoutingProvider test cases. + * + * @author Igor Wiedler + */ +class RoutingServiceProviderTest extends TestCase +{ + public function testRegister() + { + $app = new Application(); + + $app->get('/hello/{name}', function ($name) {}) + ->bind('hello'); + + $app->get('/', function () {}); + + $request = Request::create('/'); + $app->handle($request); + + $this->assertInstanceOf('Symfony\Component\Routing\Generator\UrlGenerator', $app['url_generator']); + } + + public function testUrlGeneration() + { + $app = new Application(); + + $app->get('/hello/{name}', function ($name) {}) + ->bind('hello'); + + $app->get('/', function () use ($app) { + return $app['url_generator']->generate('hello', array('name' => 'john')); + }); + + $request = Request::create('/'); + $response = $app->handle($request); + + $this->assertEquals('/hello/john', $response->getContent()); + } + + public function testAbsoluteUrlGeneration() + { + $app = new Application(); + + $app->get('/hello/{name}', function ($name) {}) + ->bind('hello'); + + $app->get('/', function () use ($app) { + return $app['url_generator']->generate('hello', array('name' => 'john'), UrlGeneratorInterface::ABSOLUTE_URL); + }); + + $request = Request::create('https://localhost:81/'); + $response = $app->handle($request); + + $this->assertEquals('https://localhost:81/hello/john', $response->getContent()); + } + + public function testUrlGenerationWithHttp() + { + $app = new Application(); + + $app->get('/insecure', function () {}) + ->bind('insecure_page') + ->requireHttp(); + + $app->get('/', function () use ($app) { + return $app['url_generator']->generate('insecure_page'); + }); + + $request = Request::create('https://localhost/'); + $response = $app->handle($request); + + $this->assertEquals('http://localhost/insecure', $response->getContent()); + } + + public function testUrlGenerationWithHttps() + { + $app = new Application(); + + $app->get('/secure', function () {}) + ->bind('secure_page') + ->requireHttps(); + + $app->get('/', function () use ($app) { + return $app['url_generator']->generate('secure_page'); + }); + + $request = Request::create('http://localhost/'); + $response = $app->handle($request); + + $this->assertEquals('https://localhost/secure', $response->getContent()); + } + + public function testControllersFactory() + { + $app = new Container(); + $app->register(new RoutingServiceProvider()); + $coll = $app['controllers_factory']; + $coll->mount('/blog', function ($blog) { + $this->assertInstanceOf('Silex\ControllerCollection', $blog); + }); + } +} diff --git a/lib/silex/vendor/silex/silex/tests/Silex/Tests/Provider/SecurityServiceProviderTest.php b/lib/silex/vendor/silex/silex/tests/Silex/Tests/Provider/SecurityServiceProviderTest.php index 90eae60f9..3436c53b1 100644 --- a/lib/silex/vendor/silex/silex/tests/Silex/Tests/Provider/SecurityServiceProviderTest.php +++ b/lib/silex/vendor/silex/silex/tests/Silex/Tests/Provider/SecurityServiceProviderTest.php @@ -16,11 +16,12 @@ use Silex\WebTestCase; use Silex\Provider\SecurityServiceProvider; use Silex\Provider\SessionServiceProvider; use Silex\Provider\ValidatorServiceProvider; +use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken; use Symfony\Component\HttpKernel\Client; use Symfony\Component\HttpFoundation\Request; /** - * SecurityServiceProvider + * SecurityServiceProvider. * * @author Fabien Potencier */ @@ -119,6 +120,28 @@ class SecurityServiceProviderTest extends WebTestCase $this->assertEquals('admin', $client->getResponse()->getContent()); } + public function testGuardAuthentication() + { + $app = $this->createApplication('guard'); + + $client = new Client($app); + + $client->request('get', '/'); + $this->assertEquals(401, $client->getResponse()->getStatusCode(), 'The entry point is configured'); + $this->assertEquals('{"message":"Authentication Required"}', $client->getResponse()->getContent()); + + $client->request('get', '/', array(), array(), array('HTTP_X_AUTH_TOKEN' => 'lili:not the secret')); + $this->assertEquals(403, $client->getResponse()->getStatusCode(), 'User not found'); + $this->assertEquals('{"message":"Username could not be found."}', $client->getResponse()->getContent()); + + $client->request('get', '/', array(), array(), array('HTTP_X_AUTH_TOKEN' => 'victoria:not the secret')); + $this->assertEquals(403, $client->getResponse()->getStatusCode(), 'Invalid credentials'); + $this->assertEquals('{"message":"Invalid credentials."}', $client->getResponse()->getContent()); + + $client->request('get', '/', array(), array(), array('HTTP_X_AUTH_TOKEN' => 'victoria:victoriasecret')); + $this->assertEquals('victoria', $client->getResponse()->getContent()); + } + public function testUserPasswordValidatorIsRegistered() { $app = new Application(); @@ -178,6 +201,146 @@ class SecurityServiceProviderTest extends WebTestCase $this->assertCount(1, unserialize(serialize($app['routes']))); } + public function testFirewallWithMethod() + { + $app = new Application(); + $app->register(new SecurityServiceProvider(), array( + 'security.firewalls' => array( + 'default' => array( + 'pattern' => '/', + 'http' => true, + 'methods' => array('POST'), + ), + ), + )); + $app->match('/', function () { return 'foo'; }) + ->method('POST|GET'); + + $request = Request::create('/', 'GET'); + $response = $app->handle($request); + $this->assertEquals(200, $response->getStatusCode()); + + $request = Request::create('/', 'POST'); + $response = $app->handle($request); + $this->assertEquals(401, $response->getStatusCode()); + } + + public function testFirewallWithHost() + { + $app = new Application(); + $app->register(new SecurityServiceProvider(), array( + 'security.firewalls' => array( + 'default' => array( + 'pattern' => '/', + 'http' => true, + 'hosts' => 'localhost2', + ), + ), + )); + $app->get('/', function () { return 'foo'; }) + ->host('localhost2'); + + $app->get('/', function () { return 'foo'; }) + ->host('localhost1'); + + $request = Request::create('http://localhost2/'); + $response = $app->handle($request); + $this->assertEquals(401, $response->getStatusCode()); + + $request = Request::create('http://localhost1/'); + $response = $app->handle($request); + $this->assertEquals(200, $response->getStatusCode()); + } + + public function testUser() + { + $app = new Application(); + $app->register(new SecurityServiceProvider(), array( + 'security.firewalls' => array( + 'default' => array( + 'http' => true, + 'users' => array( + 'fabien' => array('ROLE_ADMIN', '$2y$15$lzUNsTegNXvZW3qtfucV0erYBcEqWVeyOmjolB7R1uodsAVJ95vvu'), + ), + ), + ), + )); + $app->get('/', function () { return 'foo'; }); + + $request = Request::create('/'); + $app->handle($request); + $this->assertNull($app['user']); + + $request->headers->set('PHP_AUTH_USER', 'fabien'); + $request->headers->set('PHP_AUTH_PW', 'foo'); + $app->handle($request); + $this->assertInstanceOf('Symfony\Component\Security\Core\User\UserInterface', $app['user']); + $this->assertEquals('fabien', $app['user']->getUsername()); + } + + public function testUserWithNoToken() + { + $app = new Application(); + $app->register(new SecurityServiceProvider(), array( + 'security.firewalls' => array( + 'default' => array( + 'http' => true, + ), + ), + )); + + $request = Request::create('/'); + + $app->get('/', function () { return 'foo'; }); + $app->handle($request); + $this->assertNull($app['user']); + } + + public function testUserWithInvalidUser() + { + $app = new Application(); + $app->register(new SecurityServiceProvider(), array( + 'security.firewalls' => array( + 'default' => array( + 'http' => true, + ), + ), + )); + + $request = Request::create('/'); + $app->boot(); + $app['security.token_storage']->setToken(new UsernamePasswordToken('foo', 'foo', 'foo')); + + $app->get('/', function () { return 'foo'; }); + $app->handle($request); + $this->assertNull($app['user']); + } + + public function testAccessRulePathArray() + { + $app = new Application(); + $app->register(new SecurityServiceProvider(), array( + 'security.firewalls' => array( + 'default' => array( + 'http' => true, + ), + ), + 'security.access_rules' => array( + array(array( + 'path' => '^/admin', + ), 'ROLE_ADMIN'), + ), + )); + + $request = Request::create('/admin'); + $app->boot(); + $accessMap = $app['security.access_map']; + $this->assertEquals($accessMap->getPatterns($request), array( + array('ROLE_ADMIN'), + '', + )); + } + public function createApplication($authenticationMethod = 'form') { $app = new Application(); @@ -200,12 +363,14 @@ class SecurityServiceProviderTest extends WebTestCase 'default' => array( 'pattern' => '^.*$', 'anonymous' => true, - 'form' => true, + 'form' => array( + 'require_previous_session' => false, + ), 'logout' => true, 'users' => array( // password is foo - 'fabien' => array('ROLE_USER', '5FZ2Z8QIkA7UTZ4BYkoC+GsReLf569mSKDsfods6LYQ8t+a8EW9oaircfMpmaLbPBh4FOBiiFyLfuZmTSUwzZg=='), - 'admin' => array('ROLE_ADMIN', '5FZ2Z8QIkA7UTZ4BYkoC+GsReLf569mSKDsfods6LYQ8t+a8EW9oaircfMpmaLbPBh4FOBiiFyLfuZmTSUwzZg=='), + 'fabien' => array('ROLE_USER', '$2y$15$lzUNsTegNXvZW3qtfucV0erYBcEqWVeyOmjolB7R1uodsAVJ95vvu'), + 'admin' => array('ROLE_ADMIN', '$2y$15$lzUNsTegNXvZW3qtfucV0erYBcEqWVeyOmjolB7R1uodsAVJ95vvu'), ), ), ), @@ -224,15 +389,15 @@ class SecurityServiceProviderTest extends WebTestCase }); $app->get('/', function () use ($app) { - $user = $app['security']->getToken()->getUser(); + $user = $app['security.token_storage']->getToken()->getUser(); $content = is_object($user) ? $user->getUsername() : 'ANONYMOUS'; - if ($app['security']->isGranted('IS_AUTHENTICATED_FULLY')) { + if ($app['security.authorization_checker']->isGranted('IS_AUTHENTICATED_FULLY')) { $content .= 'AUTHENTICATED'; } - if ($app['security']->isGranted('ROLE_ADMIN')) { + if ($app['security.authorization_checker']->isGranted('ROLE_ADMIN')) { $content .= 'ADMIN'; } @@ -255,8 +420,8 @@ class SecurityServiceProviderTest extends WebTestCase 'http' => true, 'users' => array( // password is foo - 'dennis' => array('ROLE_USER', '5FZ2Z8QIkA7UTZ4BYkoC+GsReLf569mSKDsfods6LYQ8t+a8EW9oaircfMpmaLbPBh4FOBiiFyLfuZmTSUwzZg=='), - 'admin' => array('ROLE_ADMIN', '5FZ2Z8QIkA7UTZ4BYkoC+GsReLf569mSKDsfods6LYQ8t+a8EW9oaircfMpmaLbPBh4FOBiiFyLfuZmTSUwzZg=='), + 'dennis' => array('ROLE_USER', '$2y$15$lzUNsTegNXvZW3qtfucV0erYBcEqWVeyOmjolB7R1uodsAVJ95vvu'), + 'admin' => array('ROLE_ADMIN', '$2y$15$lzUNsTegNXvZW3qtfucV0erYBcEqWVeyOmjolB7R1uodsAVJ95vvu'), ), ), ), @@ -269,15 +434,14 @@ class SecurityServiceProviderTest extends WebTestCase )); $app->get('/', function () use ($app) { - $user = $app['security']->getToken()->getUser(); - + $user = $app['security.token_storage']->getToken()->getUser(); $content = is_object($user) ? $user->getUsername() : 'ANONYMOUS'; - if ($app['security']->isGranted('IS_AUTHENTICATED_FULLY')) { + if ($app['security.authorization_checker']->isGranted('IS_AUTHENTICATED_FULLY')) { $content .= 'AUTHENTICATED'; } - if ($app['security']->isGranted('ROLE_ADMIN')) { + if ($app['security.authorization_checker']->isGranted('ROLE_ADMIN')) { $content .= 'ADMIN'; } @@ -290,4 +454,38 @@ class SecurityServiceProviderTest extends WebTestCase return $app; } + + private function addGuardAuthentication($app) + { + $app['app.authenticator.token'] = function ($app) { + return new SecurityServiceProviderTest\TokenAuthenticator($app); + }; + + $app->register(new SecurityServiceProvider(), array( + 'security.firewalls' => array( + 'guard' => array( + 'pattern' => '^.*$', + 'form' => true, + 'guard' => array( + 'authenticators' => array( + 'app.authenticator.token', + ), + ), + 'users' => array( + 'victoria' => array('ROLE_USER', 'victoriasecret'), + ), + ), + ), + )); + + $app->get('/', function () use ($app) { + $user = $app['security.token_storage']->getToken()->getUser(); + + $content = is_object($user) ? $user->getUsername() : 'ANONYMOUS'; + + return $content; + })->bind('homepage'); + + return $app; + } } diff --git a/lib/silex/vendor/silex/silex/tests/Silex/Tests/Provider/SecurityServiceProviderTest/TokenAuthenticator.php b/lib/silex/vendor/silex/silex/tests/Silex/Tests/Provider/SecurityServiceProviderTest/TokenAuthenticator.php new file mode 100644 index 000000000..c569428bf --- /dev/null +++ b/lib/silex/vendor/silex/silex/tests/Silex/Tests/Provider/SecurityServiceProviderTest/TokenAuthenticator.php @@ -0,0 +1,79 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace Silex\Tests\Provider\SecurityServiceProviderTest; + +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\JsonResponse; +use Symfony\Component\Security\Core\User\UserInterface; +use Symfony\Component\Security\Core\User\UserProviderInterface; +use Symfony\Component\Security\Guard\AbstractGuardAuthenticator; +use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; +use Symfony\Component\Security\Core\Exception\AuthenticationException; + +/** + * This class is used to test "guard" authentication with the SecurityServiceProvider. + */ +class TokenAuthenticator extends AbstractGuardAuthenticator +{ + public function getCredentials(Request $request) + { + if (!$token = $request->headers->get('X-AUTH-TOKEN')) { + return; + } + + list($username, $secret) = explode(':', $token); + + return array( + 'username' => $username, + 'secret' => $secret, + ); + } + + public function getUser($credentials, UserProviderInterface $userProvider) + { + return $userProvider->loadUserByUsername($credentials['username']); + } + + public function checkCredentials($credentials, UserInterface $user) + { + // This is not a safe way of validating a password. + return $user->getPassword() === $credentials['secret']; + } + + public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey) + { + return; + } + + public function onAuthenticationFailure(Request $request, AuthenticationException $exception) + { + $data = array( + 'message' => strtr($exception->getMessageKey(), $exception->getMessageData()), + ); + + return new JsonResponse($data, 403); + } + + public function start(Request $request, AuthenticationException $authException = null) + { + $data = array( + 'message' => 'Authentication Required', + ); + + return new JsonResponse($data, 401); + } + + public function supportsRememberMe() + { + return false; + } +} diff --git a/lib/silex/vendor/silex/silex/tests/Silex/Tests/Provider/SerializerServiceProviderTest.php b/lib/silex/vendor/silex/silex/tests/Silex/Tests/Provider/SerializerServiceProviderTest.php index 0b143f5f6..c33cea7b0 100644 --- a/lib/silex/vendor/silex/silex/tests/Silex/Tests/Provider/SerializerServiceProviderTest.php +++ b/lib/silex/vendor/silex/silex/tests/Silex/Tests/Provider/SerializerServiceProviderTest.php @@ -11,6 +11,7 @@ namespace Silex\Tests\Provider; +use PHPUnit\Framework\TestCase; use Silex\Application; use Silex\Provider\SerializerServiceProvider; @@ -19,7 +20,7 @@ use Silex\Provider\SerializerServiceProvider; * * @author Fabien Potencier */ -class SerializerServiceProviderTest extends \PHPUnit_Framework_TestCase +class SerializerServiceProviderTest extends TestCase { public function testRegister() { diff --git a/lib/silex/vendor/silex/silex/tests/Silex/Tests/Provider/SessionServiceProviderTest.php b/lib/silex/vendor/silex/silex/tests/Silex/Tests/Provider/SessionServiceProviderTest.php index 7f3e00da1..fb7ae0cfd 100644 --- a/lib/silex/vendor/silex/silex/tests/Silex/Tests/Provider/SessionServiceProviderTest.php +++ b/lib/silex/vendor/silex/silex/tests/Silex/Tests/Provider/SessionServiceProviderTest.php @@ -15,6 +15,7 @@ use Silex\Application; use Silex\WebTestCase; use Silex\Provider\SessionServiceProvider; use Symfony\Component\HttpKernel\Client; +use Symfony\Component\HttpFoundation\Session; /** * SessionProvider test cases. @@ -26,7 +27,7 @@ class SessionServiceProviderTest extends WebTestCase { public function testRegister() { - /** + /* * Smoke test */ $defaultStorage = $this->app['session.storage.native']; @@ -94,7 +95,7 @@ class SessionServiceProviderTest extends WebTestCase }); $app['debug'] = true; - $app['exception_handler']->disable(); + unset($app['exception_handler']); $client = new Client($app); @@ -104,4 +105,22 @@ class SessionServiceProviderTest extends WebTestCase $client->request('get', '/robots.txt'); $this->assertEquals('Informations for robots.', $client->getResponse()->getContent()); } + + public function testSessionRegister() + { + $app = new Application(); + + $attrs = new Session\Attribute\AttributeBag(); + $flash = new Session\Flash\FlashBag(); + $app->register(new SessionServiceProvider(), array( + 'session.attribute_bag' => $attrs, + 'session.flash_bag' => $flash, + 'session.test' => true, + )); + + $session = $app['session']; + + $this->assertSame($flash, $session->getBag('flashes')); + $this->assertSame($attrs, $session->getBag('attributes')); + } } diff --git a/lib/silex/vendor/silex/silex/tests/Silex/Tests/Provider/SpoolStub.php b/lib/silex/vendor/silex/silex/tests/Silex/Tests/Provider/SpoolStub.php index 006fc0669..c1eb2b0f4 100644 --- a/lib/silex/vendor/silex/silex/tests/Silex/Tests/Provider/SpoolStub.php +++ b/lib/silex/vendor/silex/silex/tests/Silex/Tests/Provider/SpoolStub.php @@ -36,7 +36,7 @@ class SpoolStub implements \Swift_Spool public function queueMessage(\Swift_Mime_Message $message) { - $this->messages[] = $message; + $this->messages[] = clone $message; } public function flushQueue(\Swift_Transport $transport, &$failedRecipients = null) diff --git a/lib/silex/vendor/silex/silex/tests/Silex/Tests/Provider/SwiftmailerServiceProviderTest.php b/lib/silex/vendor/silex/silex/tests/Silex/Tests/Provider/SwiftmailerServiceProviderTest.php index c6ad3be55..f80c2256b 100644 --- a/lib/silex/vendor/silex/silex/tests/Silex/Tests/Provider/SwiftmailerServiceProviderTest.php +++ b/lib/silex/vendor/silex/silex/tests/Silex/Tests/Provider/SwiftmailerServiceProviderTest.php @@ -11,11 +11,12 @@ namespace Silex\Tests\Provider; +use PHPUnit\Framework\TestCase; use Silex\Application; use Silex\Provider\SwiftmailerServiceProvider; use Symfony\Component\HttpFoundation\Request; -class SwiftmailerServiceProviderTest extends \PHPUnit_Framework_TestCase +class SwiftmailerServiceProviderTest extends TestCase { public function testSwiftMailerServiceIsSwiftMailer() { @@ -37,7 +38,7 @@ class SwiftmailerServiceProviderTest extends \PHPUnit_Framework_TestCase $app['swiftmailer.use_spool'] = false; $app['swiftmailer.spooltransport'] = function () { - throw new \Exception("Should not be instantiated"); + throw new \Exception('Should not be instantiated'); }; $this->assertInstanceOf('Swift_Mailer', $app['mailer']); @@ -50,9 +51,9 @@ class SwiftmailerServiceProviderTest extends \PHPUnit_Framework_TestCase $app->register(new SwiftmailerServiceProvider()); $app->boot(); - $app['swiftmailer.spool'] = $app->share(function () { + $app['swiftmailer.spool'] = function () { return new SpoolStub(); - }); + }; $app->get('/', function () use ($app) { $app['mailer']->send(\Swift_Message::newInstance()); @@ -76,9 +77,9 @@ class SwiftmailerServiceProviderTest extends \PHPUnit_Framework_TestCase $app->register(new SwiftmailerServiceProvider()); $app->boot(); - $app['swiftmailer.spool'] = $app->share(function () { + $app['swiftmailer.spool'] = function () { return new SpoolStub(); - }); + }; $app->get('/', function () use ($app) { }); @@ -89,4 +90,45 @@ class SwiftmailerServiceProviderTest extends \PHPUnit_Framework_TestCase $app->terminate($request, $response); $this->assertFalse($app['swiftmailer.spool']->hasFlushed); } + + public function testSwiftMailerSenderAddress() + { + $app = new Application(); + + $app->register(new SwiftmailerServiceProvider()); + $app->boot(); + + $app['swiftmailer.spool'] = function () { + return new SpoolStub(); + }; + + $app['swiftmailer.sender_address'] = 'foo@example.com'; + + $app['mailer']->send(\Swift_Message::newInstance()); + + $messages = $app['swiftmailer.spool']->getMessages(); + $this->assertCount(1, $messages); + + list($message) = $messages; + $this->assertEquals('foo@example.com', $message->getReturnPath()); + } + + public function testSwiftMailerPlugins() + { + $plugin = $this->getMockBuilder('Swift_Events_TransportChangeListener')->getMock(); + $plugin->expects($this->once())->method('beforeTransportStarted'); + + $app = new Application(); + $app->boot(); + + $app->register(new SwiftmailerServiceProvider()); + + $app['swiftmailer.plugins'] = function ($app) use ($plugin) { + return array($plugin); + }; + + $dispatcher = $app['swiftmailer.transport.eventdispatcher']; + $event = $dispatcher->createTransportChangeEvent(new \Swift_Transport_NullTransport($dispatcher)); + $dispatcher->dispatchEvent($event, 'beforeTransportStarted'); + } } diff --git a/lib/silex/vendor/silex/silex/tests/Silex/Tests/Provider/TranslationServiceProviderTest.php b/lib/silex/vendor/silex/silex/tests/Silex/Tests/Provider/TranslationServiceProviderTest.php index 60838fff9..b4132f9e7 100644 --- a/lib/silex/vendor/silex/silex/tests/Silex/Tests/Provider/TranslationServiceProviderTest.php +++ b/lib/silex/vendor/silex/silex/tests/Silex/Tests/Provider/TranslationServiceProviderTest.php @@ -11,15 +11,19 @@ namespace Silex\Tests\Provider; +use PHPUnit\Framework\TestCase; use Silex\Application; use Silex\Provider\TranslationServiceProvider; +use Silex\Provider\LocaleServiceProvider; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpKernel\HttpKernelInterface; /** * TranslationProvider test cases. * * @author Daniel Tschinder */ -class TranslationServiceProviderTest extends \PHPUnit_Framework_TestCase +class TranslationServiceProviderTest extends TestCase { /** * @return Application @@ -28,6 +32,7 @@ class TranslationServiceProviderTest extends \PHPUnit_Framework_TestCase { $app = new Application(); + $app->register(new LocaleServiceProvider()); $app->register(new TranslationServiceProvider()); $app['translator.domains'] = array( 'messages' => array( @@ -102,15 +107,6 @@ class TranslationServiceProviderTest extends \PHPUnit_Framework_TestCase $this->assertEquals($expected, $result); } - public function testBackwardCompatiblityForFallback() - { - $app = $this->getPreparedApp(); - $app['locale_fallback'] = 'de'; - - $result = $app['translator']->trans('key1', array(), null, 'ru'); - $this->assertEquals('The german translation', $result); - } - public function testFallbacks() { $app = $this->getPreparedApp(); @@ -125,26 +121,62 @@ class TranslationServiceProviderTest extends \PHPUnit_Framework_TestCase $this->assertEquals('The german translation', $result); } - public function testChangingLocale() + public function testLocale() { $app = $this->getPreparedApp(); + $app->get('/', function () use ($app) { return $app['translator']->getLocale(); }); + $response = $app->handle(Request::create('/')); + $this->assertEquals('en', $response->getContent()); - $this->assertEquals('The translation', $app['translator']->trans('key1')); + $app = $this->getPreparedApp(); + $app->get('/', function () use ($app) { return $app['translator']->getLocale(); }); + $request = Request::create('/'); + $request->setLocale('fr'); + $response = $app->handle($request); + $this->assertEquals('fr', $response->getContent()); - $app['locale'] = 'de'; - - $this->assertEquals('The german translation', $app['translator']->trans('key1')); + $app = $this->getPreparedApp(); + $app->get('/{_locale}', function () use ($app) { return $app['translator']->getLocale(); }); + $response = $app->handle(Request::create('/es')); + $this->assertEquals('es', $response->getContent()); } - public function testChangingLocaleViaTranslator() + public function testLocaleInSubRequests() { $app = $this->getPreparedApp(); + $app->get('/embed/{_locale}', function () use ($app) { return $app['translator']->getLocale(); }); + $app->get('/{_locale}', function () use ($app) { + return $app['translator']->getLocale(). + $app->handle(Request::create('/embed/es'), HttpKernelInterface::SUB_REQUEST)->getContent(). + $app['translator']->getLocale(); + }); + $response = $app->handle(Request::create('/fr')); + $this->assertEquals('fresfr', $response->getContent()); - $this->assertEquals('The translation', $app['translator']->trans('key1')); + $app = $this->getPreparedApp(); + $app->get('/embed', function () use ($app) { return $app['translator']->getLocale(); }); + $app->get('/{_locale}', function () use ($app) { + return $app['translator']->getLocale(). + $app->handle(Request::create('/embed'), HttpKernelInterface::SUB_REQUEST)->getContent(). + $app['translator']->getLocale(); + }); + $response = $app->handle(Request::create('/fr')); + // locale in sub-request must be "en" as this is the value if the sub-request is converted to an ESI + $this->assertEquals('frenfr', $response->getContent()); + } - $app['translator']->setLocale('de'); - - $this->assertEquals('The german translation', $app['translator']->trans('key1')); - $this->assertEquals('de', $app['locale']); + public function testLocaleWithBefore() + { + $app = $this->getPreparedApp(); + $app->before(function (Request $request) { $request->setLocale('fr'); }, Application::EARLY_EVENT); + $app->get('/embed', function () use ($app) { return $app['translator']->getLocale(); }); + $app->get('/', function () use ($app) { + return $app['translator']->getLocale(). + $app->handle(Request::create('/embed'), HttpKernelInterface::SUB_REQUEST)->getContent(). + $app['translator']->getLocale(); + }); + $response = $app->handle(Request::create('/')); + // locale in sub-request is "en" as the before filter is only executed for the main request + $this->assertEquals('frenfr', $response->getContent()); } } diff --git a/lib/silex/vendor/silex/silex/tests/Silex/Tests/Provider/TwigServiceProviderTest.php b/lib/silex/vendor/silex/silex/tests/Silex/Tests/Provider/TwigServiceProviderTest.php index 474869833..1b5aef2c1 100644 --- a/lib/silex/vendor/silex/silex/tests/Silex/Tests/Provider/TwigServiceProviderTest.php +++ b/lib/silex/vendor/silex/silex/tests/Silex/Tests/Provider/TwigServiceProviderTest.php @@ -11,23 +11,30 @@ namespace Silex\Tests\Provider; +use Fig\Link\Link; +use PHPUnit\Framework\TestCase; use Silex\Application; +use Silex\Provider\CsrfServiceProvider; +use Silex\Provider\FormServiceProvider; use Silex\Provider\TwigServiceProvider; +use Silex\Provider\AssetServiceProvider; +use Symfony\Bridge\Twig\Extension\WebLinkExtension; use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\WebLink\HttpHeaderSerializer; /** * TwigProvider test cases. * * @author Igor Wiedler */ -class TwigServiceProviderTest extends \PHPUnit_Framework_TestCase +class TwigServiceProviderTest extends TestCase { public function testRegisterAndRender() { $app = new Application(); $app->register(new TwigServiceProvider(), array( - 'twig.templates' => array('hello' => 'Hello {{ name }}!'), + 'twig.templates' => array('hello' => 'Hello {{ name }}!'), )); $app->get('/hello/{name}', function ($name) use ($app) { @@ -39,41 +46,120 @@ class TwigServiceProviderTest extends \PHPUnit_Framework_TestCase $this->assertEquals('Hello john!', $response->getContent()); } - public function testRenderFunction() - { - $app = new Application(); - - $app->register(new TwigServiceProvider(), array( - 'twig.templates' => array( - 'hello' => '{{ render("/foo") }}', - 'foo' => 'foo', - ), - )); - - $app->get('/hello', function () use ($app) { - return $app['twig']->render('hello'); - }); - - $app->get('/foo', function () use ($app) { - return $app['twig']->render('foo'); - }); - - $request = Request::create('/hello'); - $response = $app->handle($request); - $this->assertEquals('foo', $response->getContent()); - } - public function testLoaderPriority() { $app = new Application(); $app->register(new TwigServiceProvider(), array( - 'twig.templates' => array('foo' => 'foo'), + 'twig.templates' => array('foo' => 'foo'), )); - $loader = $this->getMock('\Twig_LoaderInterface'); - $loader->expects($this->never())->method('getSource'); - $app['twig.loader.filesystem'] = $app->share(function ($app) use ($loader) { + $loader = $this->getMockBuilder('\Twig_LoaderInterface')->getMock(); + $loader->expects($this->never())->method('getSourceContext'); + $app['twig.loader.filesystem'] = function ($app) use ($loader) { return $loader; - }); - $this->assertEquals('foo', $app['twig.loader']->getSource('foo')); + }; + $this->assertEquals('foo', $app['twig.loader']->getSourceContext('foo')->getCode()); + } + + public function testHttpFoundationIntegration() + { + $app = new Application(); + $app['request_stack']->push(Request::create('/dir1/dir2/file')); + $app->register(new TwigServiceProvider(), array( + 'twig.templates' => array( + 'absolute' => '{{ absolute_url("foo.css") }}', + 'relative' => '{{ relative_path("/dir1/foo.css") }}', + ), + )); + + $this->assertEquals('http://localhost/dir1/dir2/foo.css', $app['twig']->render('absolute')); + $this->assertEquals('../foo.css', $app['twig']->render('relative')); + } + + public function testAssetIntegration() + { + $app = new Application(); + $app->register(new TwigServiceProvider(), array( + 'twig.templates' => array('hello' => '{{ asset("/foo.css") }}'), + )); + $app->register(new AssetServiceProvider(), array( + 'assets.version' => 1, + )); + + $this->assertEquals('/foo.css?1', $app['twig']->render('hello')); + } + + public function testGlobalVariable() + { + $app = new Application(); + $app['request_stack']->push(Request::create('/?name=Fabien')); + + $app->register(new TwigServiceProvider(), array( + 'twig.templates' => array('hello' => '{{ global.request.get("name") }}'), + )); + + $this->assertEquals('Fabien', $app['twig']->render('hello')); + } + + public function testFormFactory() + { + $app = new Application(); + $app->register(new FormServiceProvider()); + $app->register(new CsrfServiceProvider()); + $app->register(new TwigServiceProvider()); + + $this->assertInstanceOf('Twig_Environment', $app['twig'], 'Service twig is created successful.'); + $this->assertInstanceOf('Symfony\Bridge\Twig\Form\TwigRendererEngine', $app['twig.form.engine'], 'Service twig.form.engine is created successful.'); + $this->assertInstanceOf('Symfony\Bridge\Twig\Form\TwigRenderer', $app['twig.form.renderer'], 'Service twig.form.renderer is created successful.'); + } + + public function testFormWithoutCsrf() + { + $app = new Application(); + $app->register(new FormServiceProvider()); + $app->register(new TwigServiceProvider()); + + $this->assertInstanceOf('Twig_Environment', $app['twig']); + } + + public function testFormatParameters() + { + $app = new Application(); + + $timezone = new \DateTimeZone('Europe/Paris'); + + $app->register(new TwigServiceProvider(), array( + 'twig.date.format' => 'Y-m-d', + 'twig.date.interval_format' => '%h hours', + 'twig.date.timezone' => $timezone, + 'twig.number_format.decimals' => 2, + 'twig.number_format.decimal_point' => ',', + 'twig.number_format.thousands_separator' => ' ', + )); + + $twig = $app['twig']; + + $this->assertSame(array('Y-m-d', '%h hours'), $twig->getExtension('Twig_Extension_Core')->getDateFormat()); + $this->assertSame($timezone, $twig->getExtension('Twig_Extension_Core')->getTimezone()); + $this->assertSame(array(2, ',', ' '), $twig->getExtension('Twig_Extension_Core')->getNumberFormat()); + } + + public function testWebLinkIntegration() + { + if (!class_exists(HttpHeaderSerializer::class) || !class_exists(WebLinkExtension::class)) { + $this->markTestSkipped('Twig WebLink extension not available.'); + } + + $app = new Application(); + $app['request_stack']->push($request = Request::create('/')); + $app->register(new TwigServiceProvider(), array( + 'twig.templates' => array( + 'preload' => '{{ preload("/foo.css") }}', + ), + )); + + $this->assertEquals('/foo.css', $app['twig']->render('preload')); + + $link = new Link('preload', '/foo.css'); + $this->assertEquals(array($link), array_values($request->attributes->get('_links')->getLinks())); } } diff --git a/lib/silex/vendor/silex/silex/tests/Silex/Tests/Provider/ValidatorServiceProviderTest.php b/lib/silex/vendor/silex/silex/tests/Silex/Tests/Provider/ValidatorServiceProviderTest.php index 19d74e132..c53f349af 100644 --- a/lib/silex/vendor/silex/silex/tests/Silex/Tests/Provider/ValidatorServiceProviderTest.php +++ b/lib/silex/vendor/silex/silex/tests/Silex/Tests/Provider/ValidatorServiceProviderTest.php @@ -11,6 +11,7 @@ namespace Silex\Tests\Provider; +use PHPUnit\Framework\TestCase; use Silex\Application; use Silex\Provider\TranslationServiceProvider; use Silex\Provider\ValidatorServiceProvider; @@ -19,19 +20,21 @@ use Symfony\Component\Translation\Exception\NotFoundResourceException; use Symfony\Component\Validator\Constraints as Assert; use Silex\Tests\Provider\ValidatorServiceProviderTest\Constraint\Custom; use Silex\Tests\Provider\ValidatorServiceProviderTest\Constraint\CustomValidator; +use Symfony\Component\Validator\ValidatorInterface as LegacyValidatorInterface; +use Symfony\Component\Validator\Validator\ValidatorInterface; /** - * ValidatorServiceProvider + * ValidatorServiceProvider. * * Javier Lopez */ -class ValidatorServiceProviderTest extends \PHPUnit_Framework_TestCase +class ValidatorServiceProviderTest extends TestCase { public function testRegister() { $app = new Application(); - $app->register(new ValidatorServiceProvider()); + $app->register(new FormServiceProvider()); return $app; } @@ -40,9 +43,9 @@ class ValidatorServiceProviderTest extends \PHPUnit_Framework_TestCase { $app = new Application(); - $app['custom.validator'] = $app->share(function () { + $app['custom.validator'] = function () { return new CustomValidator(); - }); + }; $app->register(new ValidatorServiceProvider(), array( 'validator.validator_service_ids' => array( @@ -58,7 +61,7 @@ class ValidatorServiceProviderTest extends \PHPUnit_Framework_TestCase */ public function testConstraintValidatorFactory($app) { - $this->assertInstanceOf('Silex\ConstraintValidatorFactory', $app['validator.validator_factory']); + $this->assertInstanceOf('Silex\Provider\Validator\ConstraintValidatorFactory', $app['validator.validator_factory']); $validator = $app['validator.validator_factory']->getInstance(new Custom()); $this->assertInstanceOf('Silex\Tests\Provider\ValidatorServiceProviderTest\Constraint\CustomValidator', $validator); @@ -67,20 +70,27 @@ class ValidatorServiceProviderTest extends \PHPUnit_Framework_TestCase /** * @depends testRegister */ - public function testValidatorServiceIsAValidator($app) + public function testConstraintValidatorFactoryWithExpression($app) { - $this->assertInstanceOf('Symfony\Component\Validator\Validator', $app['validator']); + $constraint = new Assert\Expression('true'); + $validator = $app['validator.validator_factory']->getInstance($constraint); + $this->assertInstanceOf('Symfony\Component\Validator\Constraints\ExpressionValidator', $validator); } /** * @depends testRegister - * @dataProvider testValidatorConstraintProvider + */ + public function testValidatorServiceIsAValidator($app) + { + $this->assertTrue($app['validator'] instanceof ValidatorInterface || $app['validator'] instanceof LegacyValidatorInterface); + } + + /** + * @depends testRegister + * @dataProvider getTestValidatorConstraintProvider */ public function testValidatorConstraint($email, $isValid, $nbGlobalError, $nbEmailError, $app) { - $app->register(new ValidatorServiceProvider()); - $app->register(new FormServiceProvider()); - $constraints = new Assert\Collection(array( 'email' => array( new Assert\NotBlank(), @@ -88,21 +98,20 @@ class ValidatorServiceProviderTest extends \PHPUnit_Framework_TestCase ), )); - $builder = $app['form.factory']->createBuilder('form', array(), array( - 'constraints' => $constraints, - 'csrf_protection' => false, + $builder = $app['form.factory']->createBuilder('Symfony\Component\Form\Extension\Core\Type\FormType', array(), array( + 'constraints' => $constraints, )); $form = $builder - ->add('email', 'email', array('label' => 'Email')) + ->add('email', 'Symfony\Component\Form\Extension\Core\Type\EmailType', array('label' => 'Email')) ->getForm() ; - $form->bind(array('email' => $email)); + $form->submit(array('email' => $email)); $this->assertEquals($isValid, $form->isValid()); - $this->assertEquals($nbGlobalError, count($form->getErrors())); - $this->assertEquals($nbEmailError, count($form->offsetGet('email')->getErrors())); + $this->assertCount($nbGlobalError, $form->getErrors()); + $this->assertCount($nbEmailError, $form->offsetGet('email')->getErrors()); } public function testValidatorWillNotAddNonexistentTranslationFiles() @@ -126,13 +135,72 @@ class ValidatorServiceProviderTest extends \PHPUnit_Framework_TestCase } } - public function testValidatorConstraintProvider() + public function getTestValidatorConstraintProvider() { - // Email, form is valid , nb global error, nb email error + // Email, form is valid, nb global error, nb email error return array( array('', false, 0, 1), array('not an email', false, 0, 1), array('email@sample.com', true, 0, 0), ); } + + /** + * @dataProvider getAddResourceData + */ + public function testAddResource($registerValidatorFirst) + { + $app = new Application(); + $app['locale'] = 'fr'; + + $app->register(new ValidatorServiceProvider()); + $app->register(new TranslationServiceProvider()); + $app['translator'] = $app->extend('translator', function ($translator, $app) { + $translator->addResource('array', array('This value should not be blank.' => 'Pas vide'), 'fr', 'validators'); + + return $translator; + }); + + if ($registerValidatorFirst) { + $app['validator']; + } + + $this->assertEquals('Pas vide', $app['translator']->trans('This value should not be blank.', array(), 'validators', 'fr')); + } + + public function getAddResourceData() + { + return array(array(false), array(true)); + } + + public function testAddResourceAlternate() + { + $app = new Application(); + $app['locale'] = 'fr'; + + $app->register(new ValidatorServiceProvider()); + $app->register(new TranslationServiceProvider()); + $app->factory($app->extend('translator.resources', function ($resources, $app) { + $resources = array_merge($resources, array( + array('array', array('This value should not be blank.' => 'Pas vide'), 'fr', 'validators'), + )); + + return $resources; + })); + + $app['validator']; + + $this->assertEquals('Pas vide', $app['translator']->trans('This value should not be blank.', array(), 'validators', 'fr')); + } + + public function testTranslatorResourcesIsArray() + { + $app = new Application(); + $app['locale'] = 'fr'; + + $app->register(new ValidatorServiceProvider()); + $app->register(new TranslationServiceProvider()); + + $this->assertInternalType('array', $app['translator.resources']); + } } diff --git a/lib/silex/vendor/silex/silex/tests/Silex/Tests/Route/SecurityTraitTest.php b/lib/silex/vendor/silex/silex/tests/Silex/Tests/Route/SecurityTraitTest.php index 8f03f1d25..e984fefb5 100644 --- a/lib/silex/vendor/silex/silex/tests/Silex/Tests/Route/SecurityTraitTest.php +++ b/lib/silex/vendor/silex/silex/tests/Silex/Tests/Route/SecurityTraitTest.php @@ -11,6 +11,7 @@ namespace Silex\Tests\Route; +use PHPUnit\Framework\TestCase; use Silex\Application; use Silex\Provider\SecurityServiceProvider; use Symfony\Component\HttpFoundation\Request; @@ -19,10 +20,8 @@ use Symfony\Component\HttpFoundation\Request; * SecurityTrait test cases. * * @author Fabien Potencier - * - * @requires PHP 5.4 */ -class SecurityTraitTest extends \PHPUnit_Framework_TestCase +class SecurityTraitTest extends TestCase { public function testSecureWithNoAuthenticatedUser() { @@ -76,7 +75,7 @@ class SecurityTraitTest extends \PHPUnit_Framework_TestCase 'default' => array( 'http' => true, 'users' => array( - 'fabien' => array('ROLE_ADMIN', '5FZ2Z8QIkA7UTZ4BYkoC+GsReLf569mSKDsfods6LYQ8t+a8EW9oaircfMpmaLbPBh4FOBiiFyLfuZmTSUwzZg=='), + 'fabien' => array('ROLE_ADMIN', '$2y$15$lzUNsTegNXvZW3qtfucV0erYBcEqWVeyOmjolB7R1uodsAVJ95vvu'), ), ), ), diff --git a/lib/silex/vendor/silex/silex/tests/Silex/Tests/RouterTest.php b/lib/silex/vendor/silex/silex/tests/Silex/Tests/RouterTest.php index 2644693bd..0d0bf518b 100644 --- a/lib/silex/vendor/silex/silex/tests/Silex/Tests/RouterTest.php +++ b/lib/silex/vendor/silex/silex/tests/Silex/Tests/RouterTest.php @@ -11,6 +11,7 @@ namespace Silex\Tests; +use PHPUnit\Framework\TestCase; use Silex\Application; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; @@ -21,7 +22,7 @@ use Symfony\Component\HttpFoundation\RedirectResponse; * * @author Igor Wiedler */ -class RouterTest extends \PHPUnit_Framework_TestCase +class RouterTest extends TestCase { public function testMapRouting() { @@ -100,7 +101,7 @@ class RouterTest extends \PHPUnit_Framework_TestCase public function testMissingRoute() { $app = new Application(); - $app['exception_handler']->disable(); + unset($app['exception_handler']); $request = Request::create('/baz'); $app->handle($request); @@ -152,12 +153,12 @@ class RouterTest extends \PHPUnit_Framework_TestCase { $app = new Application(); - $app->get('/foo', function () use ($app) { - return new Response($app['request']->getRequestUri()); + $app->get('/foo', function (Request $request) use ($app) { + return new Response($request->getRequestUri()); }); - $app->error(function ($e) use ($app) { - return new Response($app['request']->getRequestUri()); + $app->error(function ($e, Request $request, $code) use ($app) { + return new Response($request->getRequestUri()); }); foreach (array('/foo', '/bar') as $path) { @@ -232,6 +233,19 @@ class RouterTest extends \PHPUnit_Framework_TestCase $this->assertTrue($response->isRedirect('https://example.com/secured?query=string')); } + public function testConditionOnRoute() + { + $app = new Application(); + $app->match('/secured', function () { + return 'secured content'; + }) + ->when('request.isSecure() == true'); + + $request = Request::create('http://example.com/secured'); + $response = $app->handle($request); + $this->assertEquals(404, $response->getStatusCode()); + } + public function testClassNameControllerSyntax() { $app = new Application(); diff --git a/lib/silex/vendor/silex/silex/tests/Silex/Tests/ServiceControllerResolverRouterTest.php b/lib/silex/vendor/silex/silex/tests/Silex/Tests/ServiceControllerResolverRouterTest.php index db708181c..4bc88a451 100644 --- a/lib/silex/vendor/silex/silex/tests/Silex/Tests/ServiceControllerResolverRouterTest.php +++ b/lib/silex/vendor/silex/silex/tests/Silex/Tests/ServiceControllerResolverRouterTest.php @@ -16,13 +16,14 @@ use Silex\Provider\ServiceControllerServiceProvider; use Symfony\Component\HttpFoundation\Request; /** - * Router test cases, using the ServiceControllerResolver + * Router test cases, using the ServiceControllerResolver. */ class ServiceControllerResolverRouterTest extends RouterTest { public function testServiceNameControllerSyntax() { $app = new Application(); + $app->register(new ServiceControllerServiceProvider()); $app['service_name'] = function () { return new MyController(); @@ -35,8 +36,6 @@ class ServiceControllerResolverRouterTest extends RouterTest protected function checkRouteResponse(Application $app, $path, $expectedContent, $method = 'get', $message = null) { - $app->register(new ServiceControllerServiceProvider()); - $request = Request::create($path, $method); $response = $app->handle($request); $this->assertEquals($expectedContent, $response->getContent(), $message); diff --git a/lib/silex/vendor/silex/silex/tests/Silex/Tests/ServiceControllerResolverTest.php b/lib/silex/vendor/silex/silex/tests/Silex/Tests/ServiceControllerResolverTest.php index fda9dba0c..a2bf35d30 100644 --- a/lib/silex/vendor/silex/silex/tests/Silex/Tests/ServiceControllerResolverTest.php +++ b/lib/silex/vendor/silex/silex/tests/Silex/Tests/ServiceControllerResolverTest.php @@ -11,16 +11,23 @@ namespace Silex\Tests; +use PHPUnit\Framework\TestCase; use Silex\ServiceControllerResolver; use Silex\Application; use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpKernel\Kernel; /** * Unit tests for ServiceControllerResolver, see ServiceControllerResolverRouterTest for some - * integration tests + * integration tests. */ -class ServiceControllerResolverTest extends \PHPUnit_Framework_Testcase +class ServiceControllerResolverTest extends Testcase { + private $app; + private $mockCallbackResolver; + private $mockResolver; + private $resolver; + public function setup() { $this->mockResolver = $this->getMockBuilder('Symfony\Component\HttpKernel\Controller\ControllerResolverInterface') @@ -71,8 +78,15 @@ class ServiceControllerResolverTest extends \PHPUnit_Framework_Testcase $this->assertEquals(123, $this->resolver->getController($req)); } + /** + * @group legacy + */ public function testShouldDelegateGetArguments() { + if (Kernel::VERSION_ID >= 40000) { + self::markTestSkipped('HttpKernel < 4.0 is required'); + } + $req = Request::create('/'); $this->mockResolver->expects($this->once()) ->method('getArguments') diff --git a/lib/silex/vendor/silex/silex/tests/Silex/Tests/StreamTest.php b/lib/silex/vendor/silex/silex/tests/Silex/Tests/StreamTest.php index 7a25b6678..24c26b4cf 100644 --- a/lib/silex/vendor/silex/silex/tests/Silex/Tests/StreamTest.php +++ b/lib/silex/vendor/silex/silex/tests/Silex/Tests/StreamTest.php @@ -11,6 +11,7 @@ namespace Silex\Tests; +use PHPUnit\Framework\TestCase; use Silex\Application; use Symfony\Component\HttpFoundation\Request; @@ -19,7 +20,7 @@ use Symfony\Component\HttpFoundation\Request; * * @author Igor Wiedler */ -class StreamTest extends \PHPUnit_Framework_TestCase +class StreamTest extends TestCase { public function testStreamReturnsStreamingResponse() { @@ -27,7 +28,7 @@ class StreamTest extends \PHPUnit_Framework_TestCase $response = $app->stream(); $this->assertInstanceOf('Symfony\Component\HttpFoundation\StreamedResponse', $response); - $this->assertSame(false, $response->getContent()); + $this->assertFalse($response->getContent()); } public function testStreamActuallyStreams() @@ -35,7 +36,7 @@ class StreamTest extends \PHPUnit_Framework_TestCase $i = 0; $stream = function () use (&$i) { - $i++; + ++$i; }; $app = new Application(); diff --git a/lib/silex/vendor/silex/silex/tests/Silex/Tests/WebTestCaseTest.php b/lib/silex/vendor/silex/silex/tests/Silex/Tests/WebTestCaseTest.php index e26af50f9..474ffc389 100644 --- a/lib/silex/vendor/silex/silex/tests/Silex/Tests/WebTestCaseTest.php +++ b/lib/silex/vendor/silex/silex/tests/Silex/Tests/WebTestCaseTest.php @@ -13,6 +13,7 @@ namespace Silex\Tests; use Silex\Application; use Silex\WebTestCase; +use Symfony\Component\HttpFoundation\Request; /** * Functional test cases. @@ -33,9 +34,9 @@ class WebTestCaseTest extends WebTestCase return '

title

'; }); - $app->match('/server', function () use ($app) { - $user = $app['request']->server->get('PHP_AUTH_USER'); - $pass = $app['request']->server->get('PHP_AUTH_PW'); + $app->match('/server', function (Request $request) use ($app) { + $user = $request->server->get('PHP_AUTH_USER'); + $pass = $request->server->get('PHP_AUTH_PW'); return "

$user:$pass

"; }); @@ -68,7 +69,7 @@ class WebTestCaseTest extends WebTestCase $client = $this->createClient(array( 'PHP_AUTH_USER' => $user, - 'PHP_AUTH_PW' => $pass, + 'PHP_AUTH_PW' => $pass, )); $crawler = $client->request('GET', '/server'); diff --git a/lib/silex/vendor/symfony/debug/.gitignore b/lib/silex/vendor/symfony/debug/.gitignore new file mode 100644 index 000000000..c49a5d8df --- /dev/null +++ b/lib/silex/vendor/symfony/debug/.gitignore @@ -0,0 +1,3 @@ +vendor/ +composer.lock +phpunit.xml diff --git a/lib/silex/vendor/symfony/debug/BufferingLogger.php b/lib/silex/vendor/symfony/debug/BufferingLogger.php new file mode 100644 index 000000000..a2ed75b9d --- /dev/null +++ b/lib/silex/vendor/symfony/debug/BufferingLogger.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\Debug; + +use Psr\Log\AbstractLogger; + +/** + * A buffering logger that stacks logs for later. + * + * @author Nicolas Grekas + */ +class BufferingLogger extends AbstractLogger +{ + private $logs = array(); + + public function log($level, $message, array $context = array()) + { + $this->logs[] = array($level, $message, $context); + } + + public function cleanLogs() + { + $logs = $this->logs; + $this->logs = array(); + + return $logs; + } +} diff --git a/lib/silex/vendor/symfony/debug/CHANGELOG.md b/lib/silex/vendor/symfony/debug/CHANGELOG.md new file mode 100644 index 000000000..31c67eb60 --- /dev/null +++ b/lib/silex/vendor/symfony/debug/CHANGELOG.md @@ -0,0 +1,64 @@ +CHANGELOG +========= + +3.4.0 +----- + +* deprecated `ErrorHandler::stackErrors()` and `ErrorHandler::unstackErrors()` + +3.3.0 +----- + +* deprecated the `ContextErrorException` class: use \ErrorException directly now + +3.2.0 +----- + +* `FlattenException::getTrace()` now returns additional type descriptions + `integer` and `float`. + + +3.0.0 +----- + +* removed classes, methods and interfaces deprecated in 2.x + +2.8.0 +----- + +* added BufferingLogger for errors that happen before a proper logger is configured +* allow throwing from `__toString()` with `return trigger_error($e, E_USER_ERROR);` +* deprecate ExceptionHandler::createResponse + +2.7.0 +----- + +* added deprecations checking for parent interfaces/classes to DebugClassLoader +* added ZTS support to symfony_debug extension +* added symfony_debug_backtrace() to symfony_debug extension + to track the backtrace of fatal errors + +2.6.0 +----- + +* generalized ErrorHandler and ExceptionHandler, + with some new methods and others deprecated +* enhanced error messages for uncaught exceptions + +2.5.0 +----- + +* added ExceptionHandler::setHandler() +* added UndefinedMethodFatalErrorHandler +* deprecated DummyException + +2.4.0 +----- + + * added a DebugClassLoader able to wrap any autoloader providing a findFile method + * improved error messages for not found classes and functions + +2.3.0 +----- + + * added the component diff --git a/lib/silex/vendor/symfony/debug/Debug.php b/lib/silex/vendor/symfony/debug/Debug.php new file mode 100644 index 000000000..e3665ae5f --- /dev/null +++ b/lib/silex/vendor/symfony/debug/Debug.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\Debug; + +/** + * Registers all the debug tools. + * + * @author Fabien Potencier + */ +class Debug +{ + private static $enabled = false; + + /** + * Enables the debug tools. + * + * This method registers an error handler and an exception handler. + * + * If the Symfony ClassLoader component is available, a special + * class loader is also registered. + * + * @param int $errorReportingLevel The level of error reporting you want + * @param bool $displayErrors Whether to display errors (for development) or just log them (for production) + */ + public static function enable($errorReportingLevel = E_ALL, $displayErrors = true) + { + if (static::$enabled) { + return; + } + + static::$enabled = true; + + if (null !== $errorReportingLevel) { + error_reporting($errorReportingLevel); + } else { + error_reporting(E_ALL); + } + + if ('cli' !== PHP_SAPI) { + ini_set('display_errors', 0); + ExceptionHandler::register(); + } elseif ($displayErrors && (!ini_get('log_errors') || ini_get('error_log'))) { + // CLI - display errors only if they're not already logged to STDERR + ini_set('display_errors', 1); + } + if ($displayErrors) { + ErrorHandler::register(new ErrorHandler(new BufferingLogger())); + } else { + ErrorHandler::register()->throwAt(0, true); + } + + DebugClassLoader::enable(); + } +} diff --git a/lib/silex/vendor/symfony/debug/DebugClassLoader.php b/lib/silex/vendor/symfony/debug/DebugClassLoader.php new file mode 100644 index 000000000..78bfb8538 --- /dev/null +++ b/lib/silex/vendor/symfony/debug/DebugClassLoader.php @@ -0,0 +1,409 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Debug; + +/** + * Autoloader checking if the class is really defined in the file found. + * + * The ClassLoader will wrap all registered autoloaders + * and will throw an exception if a file is found but does + * not declare the class. + * + * @author Fabien Potencier + * @author Christophe Coevoet + * @author Nicolas Grekas + */ +class DebugClassLoader +{ + private $classLoader; + private $isFinder; + private $loaded = array(); + private static $caseCheck; + private static $checkedClasses = array(); + private static $final = array(); + private static $finalMethods = array(); + private static $deprecated = array(); + private static $internal = array(); + private static $internalMethods = array(); + private static $php7Reserved = array('int' => 1, 'float' => 1, 'bool' => 1, 'string' => 1, 'true' => 1, 'false' => 1, 'null' => 1); + private static $darwinCache = array('/' => array('/', array())); + + public function __construct(callable $classLoader) + { + $this->classLoader = $classLoader; + $this->isFinder = is_array($classLoader) && method_exists($classLoader[0], 'findFile'); + + if (!isset(self::$caseCheck)) { + $file = file_exists(__FILE__) ? __FILE__ : rtrim(realpath('.'), DIRECTORY_SEPARATOR); + $i = strrpos($file, DIRECTORY_SEPARATOR); + $dir = substr($file, 0, 1 + $i); + $file = substr($file, 1 + $i); + $test = strtoupper($file) === $file ? strtolower($file) : strtoupper($file); + $test = realpath($dir.$test); + + if (false === $test || false === $i) { + // filesystem is case sensitive + self::$caseCheck = 0; + } elseif (substr($test, -strlen($file)) === $file) { + // filesystem is case insensitive and realpath() normalizes the case of characters + self::$caseCheck = 1; + } elseif (false !== stripos(PHP_OS, 'darwin')) { + // on MacOSX, HFS+ is case insensitive but realpath() doesn't normalize the case of characters + self::$caseCheck = 2; + } else { + // filesystem case checks failed, fallback to disabling them + self::$caseCheck = 0; + } + } + } + + /** + * Gets the wrapped class loader. + * + * @return callable The wrapped class loader + */ + public function getClassLoader() + { + return $this->classLoader; + } + + /** + * Wraps all autoloaders. + */ + public static function enable() + { + // Ensures we don't hit https://bugs.php.net/42098 + class_exists('Symfony\Component\Debug\ErrorHandler'); + class_exists('Psr\Log\LogLevel'); + + if (!is_array($functions = spl_autoload_functions())) { + return; + } + + foreach ($functions as $function) { + spl_autoload_unregister($function); + } + + foreach ($functions as $function) { + if (!is_array($function) || !$function[0] instanceof self) { + $function = array(new static($function), 'loadClass'); + } + + spl_autoload_register($function); + } + } + + /** + * Disables the wrapping. + */ + public static function disable() + { + if (!is_array($functions = spl_autoload_functions())) { + return; + } + + foreach ($functions as $function) { + spl_autoload_unregister($function); + } + + foreach ($functions as $function) { + if (is_array($function) && $function[0] instanceof self) { + $function = $function[0]->getClassLoader(); + } + + spl_autoload_register($function); + } + } + + /** + * Loads the given class or interface. + * + * @param string $class The name of the class + * + * @return bool|null True, if loaded + * + * @throws \RuntimeException + */ + public function loadClass($class) + { + $e = error_reporting(error_reporting() | E_PARSE | E_ERROR | E_CORE_ERROR | E_COMPILE_ERROR); + + try { + if ($this->isFinder && !isset($this->loaded[$class])) { + $this->loaded[$class] = true; + if ($file = $this->classLoader[0]->findFile($class) ?: false) { + $wasCached = \function_exists('opcache_is_script_cached') && opcache_is_script_cached($file); + + require $file; + + if ($wasCached) { + return; + } + } + } else { + call_user_func($this->classLoader, $class); + $file = false; + } + } finally { + error_reporting($e); + } + + $this->checkClass($class, $file); + } + + private function checkClass($class, $file = null) + { + $exists = null === $file || \class_exists($class, false) || \interface_exists($class, false) || \trait_exists($class, false); + + if (null !== $file && $class && '\\' === $class[0]) { + $class = substr($class, 1); + } + + if ($exists) { + if (isset(self::$checkedClasses[$class])) { + return; + } + self::$checkedClasses[$class] = true; + + $refl = new \ReflectionClass($class); + if (null === $file && $refl->isInternal()) { + return; + } + $name = $refl->getName(); + + if ($name !== $class && 0 === \strcasecmp($name, $class)) { + throw new \RuntimeException(sprintf('Case mismatch between loaded and declared class names: "%s" vs "%s".', $class, $name)); + } + + // Don't trigger deprecations for classes in the same vendor + if (2 > $len = 1 + (\strpos($name, '\\') ?: \strpos($name, '_'))) { + $len = 0; + $ns = ''; + } else { + $ns = \substr($name, 0, $len); + } + + // Detect annotations on the class + if (false !== $doc = $refl->getDocComment()) { + foreach (array('final', 'deprecated', 'internal') as $annotation) { + if (false !== \strpos($doc, $annotation) && preg_match('#\n \* @'.$annotation.'(?:( .+?)\.?)?\r?\n \*(?: @|/$)#s', $doc, $notice)) { + self::${$annotation}[$name] = isset($notice[1]) ? preg_replace('#\s*\r?\n \* +#', ' ', $notice[1]) : ''; + } + } + } + + $parentAndTraits = \class_uses($name, false); + if ($parent = \get_parent_class($class)) { + $parentAndTraits[] = $parent; + + if (!isset(self::$checkedClasses[$parent])) { + $this->checkClass($parent); + } + + if (isset(self::$final[$parent])) { + @trigger_error(sprintf('The "%s" class is considered final%s. It may change without further notice as of its next major version. You should not extend it from "%s".', $parent, self::$final[$parent], $name), E_USER_DEPRECATED); + } + } + + // Detect if the parent is annotated + foreach ($parentAndTraits + $this->getOwnInterfaces($name, $parent) as $use) { + if (!isset(self::$checkedClasses[$use])) { + $this->checkClass($use); + } + if (isset(self::$deprecated[$use]) && \strncmp($ns, $use, $len)) { + $type = class_exists($name, false) ? 'class' : (interface_exists($name, false) ? 'interface' : 'trait'); + $verb = class_exists($use, false) || interface_exists($name, false) ? 'extends' : (interface_exists($use, false) ? 'implements' : 'uses'); + + @trigger_error(sprintf('The "%s" %s %s "%s" that is deprecated%s.', $name, $type, $verb, $use, self::$deprecated[$use]), E_USER_DEPRECATED); + } + if (isset(self::$internal[$use]) && \strncmp($ns, $use, $len)) { + @trigger_error(sprintf('The "%s" %s is considered internal%s. It may change without further notice. You should not use it from "%s".', $use, class_exists($use, false) ? 'class' : (interface_exists($use, false) ? 'interface' : 'trait'), self::$internal[$use], $name), E_USER_DEPRECATED); + } + } + + // Inherit @final and @internal annotations for methods + self::$finalMethods[$name] = array(); + self::$internalMethods[$name] = array(); + foreach ($parentAndTraits as $use) { + foreach (array('finalMethods', 'internalMethods') as $property) { + if (isset(self::${$property}[$use])) { + self::${$property}[$name] = self::${$property}[$name] ? self::${$property}[$use] + self::${$property}[$name] : self::${$property}[$use]; + } + } + } + + $isClass = \class_exists($name, false); + foreach ($refl->getMethods(\ReflectionMethod::IS_PUBLIC | \ReflectionMethod::IS_PROTECTED) as $method) { + if ($method->class !== $name) { + continue; + } + + // Method from a trait + if ($method->getFilename() !== $refl->getFileName()) { + continue; + } + + if ($isClass && $parent && isset(self::$finalMethods[$parent][$method->name])) { + list($declaringClass, $message) = self::$finalMethods[$parent][$method->name]; + @trigger_error(sprintf('The "%s::%s()" method is considered final%s. It may change without further notice as of its next major version. You should not extend it from "%s".', $declaringClass, $method->name, $message, $name), E_USER_DEPRECATED); + } + + foreach ($parentAndTraits as $use) { + if (isset(self::$internalMethods[$use][$method->name])) { + list($declaringClass, $message) = self::$internalMethods[$use][$method->name]; + if (\strncmp($ns, $declaringClass, $len)) { + @trigger_error(sprintf('The "%s::%s()" method is considered internal%s. It may change without further notice. You should not extend it from "%s".', $declaringClass, $method->name, $message, $name), E_USER_DEPRECATED); + } + } + } + + // Detect method annotations + if (false === $doc = $method->getDocComment()) { + continue; + } + + foreach (array('final', 'internal') as $annotation) { + if (false !== \strpos($doc, $annotation) && preg_match('#\n\s+\* @'.$annotation.'(?:( .+?)\.?)?\r?\n\s+\*(?: @|/$)#s', $doc, $notice)) { + $message = isset($notice[1]) ? preg_replace('#\s*\r?\n \* +#', ' ', $notice[1]) : ''; + self::${$annotation.'Methods'}[$name][$method->name] = array($name, $message); + } + } + } + + if (isset(self::$php7Reserved[\strtolower($refl->getShortName())])) { + @trigger_error(sprintf('The "%s" class uses the reserved name "%s", it will break on PHP 7 and higher', $name, $refl->getShortName()), E_USER_DEPRECATED); + } + } + + if ($file) { + if (!$exists) { + if (false !== strpos($class, '/')) { + throw new \RuntimeException(sprintf('Trying to autoload a class with an invalid name "%s". Be careful that the namespace separator is "\" in PHP, not "/".', $class)); + } + + throw new \RuntimeException(sprintf('The autoloader expected class "%s" to be defined in file "%s". The file was found but the class was not in it, the class name or namespace probably has a typo.', $class, $file)); + } + if (self::$caseCheck) { + $real = explode('\\', $class.strrchr($file, '.')); + $tail = explode(DIRECTORY_SEPARATOR, str_replace('/', DIRECTORY_SEPARATOR, $file)); + + $i = count($tail) - 1; + $j = count($real) - 1; + + while (isset($tail[$i], $real[$j]) && $tail[$i] === $real[$j]) { + --$i; + --$j; + } + + array_splice($tail, 0, $i + 1); + } + if (self::$caseCheck && $tail) { + $tail = DIRECTORY_SEPARATOR.implode(DIRECTORY_SEPARATOR, $tail); + $tailLen = strlen($tail); + $real = $refl->getFileName(); + + if (2 === self::$caseCheck) { + // realpath() on MacOSX doesn't normalize the case of characters + + $i = 1 + strrpos($real, '/'); + $file = substr($real, $i); + $real = substr($real, 0, $i); + + if (isset(self::$darwinCache[$real])) { + $kDir = $real; + } else { + $kDir = strtolower($real); + + if (isset(self::$darwinCache[$kDir])) { + $real = self::$darwinCache[$kDir][0]; + } else { + $dir = getcwd(); + chdir($real); + $real = getcwd().'/'; + chdir($dir); + + $dir = $real; + $k = $kDir; + $i = strlen($dir) - 1; + while (!isset(self::$darwinCache[$k])) { + self::$darwinCache[$k] = array($dir, array()); + self::$darwinCache[$dir] = &self::$darwinCache[$k]; + + while ('/' !== $dir[--$i]) { + } + $k = substr($k, 0, ++$i); + $dir = substr($dir, 0, $i--); + } + } + } + + $dirFiles = self::$darwinCache[$kDir][1]; + + if (isset($dirFiles[$file])) { + $kFile = $file; + } else { + $kFile = strtolower($file); + + if (!isset($dirFiles[$kFile])) { + foreach (scandir($real, 2) as $f) { + if ('.' !== $f[0]) { + $dirFiles[$f] = $f; + if ($f === $file) { + $kFile = $k = $file; + } elseif ($f !== $k = strtolower($f)) { + $dirFiles[$k] = $f; + } + } + } + self::$darwinCache[$kDir][1] = $dirFiles; + } + } + + $real .= $dirFiles[$kFile]; + } + + if (0 === substr_compare($real, $tail, -$tailLen, $tailLen, true) + && 0 !== substr_compare($real, $tail, -$tailLen, $tailLen, false) + ) { + throw new \RuntimeException(sprintf('Case mismatch between class and real file names: "%s" vs "%s" in "%s".', substr($tail, -$tailLen + 1), substr($real, -$tailLen + 1), substr($real, 0, -$tailLen + 1))); + } + } + } + } + + /** + * `class_implements` includes interfaces from the parents so we have to manually exclude them. + * + * @param string $class + * @param string|false $parent + * + * @return string[] + */ + private function getOwnInterfaces($class, $parent) + { + $ownInterfaces = class_implements($class, false); + + if ($parent) { + foreach (class_implements($parent, false) as $interface) { + unset($ownInterfaces[$interface]); + } + } + + foreach ($ownInterfaces as $interface) { + foreach (class_implements($interface) as $interface) { + unset($ownInterfaces[$interface]); + } + } + + return $ownInterfaces; + } +} diff --git a/lib/silex/vendor/symfony/debug/ErrorHandler.php b/lib/silex/vendor/symfony/debug/ErrorHandler.php new file mode 100644 index 000000000..90e5535d9 --- /dev/null +++ b/lib/silex/vendor/symfony/debug/ErrorHandler.php @@ -0,0 +1,758 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Debug; + +use Psr\Log\LogLevel; +use Psr\Log\LoggerInterface; +use Symfony\Component\Debug\Exception\ContextErrorException; +use Symfony\Component\Debug\Exception\FatalErrorException; +use Symfony\Component\Debug\Exception\FatalThrowableError; +use Symfony\Component\Debug\Exception\OutOfMemoryException; +use Symfony\Component\Debug\Exception\SilencedErrorContext; +use Symfony\Component\Debug\FatalErrorHandler\UndefinedFunctionFatalErrorHandler; +use Symfony\Component\Debug\FatalErrorHandler\UndefinedMethodFatalErrorHandler; +use Symfony\Component\Debug\FatalErrorHandler\ClassNotFoundFatalErrorHandler; +use Symfony\Component\Debug\FatalErrorHandler\FatalErrorHandlerInterface; + +/** + * A generic ErrorHandler for the PHP engine. + * + * Provides five bit fields that control how errors are handled: + * - thrownErrors: errors thrown as \ErrorException + * - loggedErrors: logged errors, when not @-silenced + * - scopedErrors: errors thrown or logged with their local context + * - tracedErrors: errors logged with their stack trace + * - screamedErrors: never @-silenced errors + * + * Each error level can be logged by a dedicated PSR-3 logger object. + * Screaming only applies to logging. + * Throwing takes precedence over logging. + * Uncaught exceptions are logged as E_ERROR. + * E_DEPRECATED and E_USER_DEPRECATED levels never throw. + * E_RECOVERABLE_ERROR and E_USER_ERROR levels always throw. + * Non catchable errors that can be detected at shutdown time are logged when the scream bit field allows so. + * As errors have a performance cost, repeated errors are all logged, so that the developer + * can see them and weight them as more important to fix than others of the same level. + * + * @author Nicolas Grekas + * @author Grégoire Pineau + */ +class ErrorHandler +{ + private $levels = array( + E_DEPRECATED => 'Deprecated', + E_USER_DEPRECATED => 'User Deprecated', + E_NOTICE => 'Notice', + E_USER_NOTICE => 'User Notice', + E_STRICT => 'Runtime Notice', + E_WARNING => 'Warning', + E_USER_WARNING => 'User Warning', + E_COMPILE_WARNING => 'Compile Warning', + E_CORE_WARNING => 'Core Warning', + E_USER_ERROR => 'User Error', + E_RECOVERABLE_ERROR => 'Catchable Fatal Error', + E_COMPILE_ERROR => 'Compile Error', + E_PARSE => 'Parse Error', + E_ERROR => 'Error', + E_CORE_ERROR => 'Core Error', + ); + + private $loggers = array( + E_DEPRECATED => array(null, LogLevel::INFO), + E_USER_DEPRECATED => array(null, LogLevel::INFO), + E_NOTICE => array(null, LogLevel::WARNING), + E_USER_NOTICE => array(null, LogLevel::WARNING), + E_STRICT => array(null, LogLevel::WARNING), + E_WARNING => array(null, LogLevel::WARNING), + E_USER_WARNING => array(null, LogLevel::WARNING), + E_COMPILE_WARNING => array(null, LogLevel::WARNING), + E_CORE_WARNING => array(null, LogLevel::WARNING), + E_USER_ERROR => array(null, LogLevel::CRITICAL), + E_RECOVERABLE_ERROR => array(null, LogLevel::CRITICAL), + E_COMPILE_ERROR => array(null, LogLevel::CRITICAL), + E_PARSE => array(null, LogLevel::CRITICAL), + E_ERROR => array(null, LogLevel::CRITICAL), + E_CORE_ERROR => array(null, LogLevel::CRITICAL), + ); + + private $thrownErrors = 0x1FFF; // E_ALL - E_DEPRECATED - E_USER_DEPRECATED + private $scopedErrors = 0x1FFF; // E_ALL - E_DEPRECATED - E_USER_DEPRECATED + private $tracedErrors = 0x77FB; // E_ALL - E_STRICT - E_PARSE + private $screamedErrors = 0x55; // E_ERROR + E_CORE_ERROR + E_COMPILE_ERROR + E_PARSE + private $loggedErrors = 0; + private $traceReflector; + + private $isRecursive = 0; + private $isRoot = false; + private $exceptionHandler; + private $bootstrappingLogger; + + private static $reservedMemory; + private static $stackedErrors = array(); + private static $stackedErrorLevels = array(); + private static $toStringException = null; + private static $silencedErrorCache = array(); + private static $silencedErrorCount = 0; + private static $exitCode = 0; + + /** + * Registers the error handler. + * + * @param self|null $handler The handler to register + * @param bool $replace Whether to replace or not any existing handler + * + * @return self The registered error handler + */ + public static function register(self $handler = null, $replace = true) + { + if (null === self::$reservedMemory) { + self::$reservedMemory = str_repeat('x', 10240); + register_shutdown_function(__CLASS__.'::handleFatalError'); + } + + if ($handlerIsNew = null === $handler) { + $handler = new static(); + } + + if (null === $prev = set_error_handler(array($handler, 'handleError'))) { + restore_error_handler(); + // Specifying the error types earlier would expose us to https://bugs.php.net/63206 + set_error_handler(array($handler, 'handleError'), $handler->thrownErrors | $handler->loggedErrors); + $handler->isRoot = true; + } + + if ($handlerIsNew && is_array($prev) && $prev[0] instanceof self) { + $handler = $prev[0]; + $replace = false; + } + if (!$replace && $prev) { + restore_error_handler(); + } + if (is_array($prev = set_exception_handler(array($handler, 'handleException'))) && $prev[0] === $handler) { + restore_exception_handler(); + } else { + $handler->setExceptionHandler($prev); + } + + $handler->throwAt(E_ALL & $handler->thrownErrors, true); + + return $handler; + } + + public function __construct(BufferingLogger $bootstrappingLogger = null) + { + if ($bootstrappingLogger) { + $this->bootstrappingLogger = $bootstrappingLogger; + $this->setDefaultLogger($bootstrappingLogger); + } + $this->traceReflector = new \ReflectionProperty('Exception', 'trace'); + $this->traceReflector->setAccessible(true); + } + + /** + * Sets a logger to non assigned errors levels. + * + * @param LoggerInterface $logger A PSR-3 logger to put as default for the given levels + * @param array|int $levels An array map of E_* to LogLevel::* or an integer bit field of E_* constants + * @param bool $replace Whether to replace or not any existing logger + */ + public function setDefaultLogger(LoggerInterface $logger, $levels = E_ALL, $replace = false) + { + $loggers = array(); + + if (is_array($levels)) { + foreach ($levels as $type => $logLevel) { + if (empty($this->loggers[$type][0]) || $replace || $this->loggers[$type][0] === $this->bootstrappingLogger) { + $loggers[$type] = array($logger, $logLevel); + } + } + } else { + if (null === $levels) { + $levels = E_ALL; + } + foreach ($this->loggers as $type => $log) { + if (($type & $levels) && (empty($log[0]) || $replace || $log[0] === $this->bootstrappingLogger)) { + $log[0] = $logger; + $loggers[$type] = $log; + } + } + } + + $this->setLoggers($loggers); + } + + /** + * Sets a logger for each error level. + * + * @param array $loggers Error levels to [LoggerInterface|null, LogLevel::*] map + * + * @return array The previous map + * + * @throws \InvalidArgumentException + */ + public function setLoggers(array $loggers) + { + $prevLogged = $this->loggedErrors; + $prev = $this->loggers; + $flush = array(); + + foreach ($loggers as $type => $log) { + if (!isset($prev[$type])) { + throw new \InvalidArgumentException('Unknown error type: '.$type); + } + if (!is_array($log)) { + $log = array($log); + } elseif (!array_key_exists(0, $log)) { + throw new \InvalidArgumentException('No logger provided'); + } + if (null === $log[0]) { + $this->loggedErrors &= ~$type; + } elseif ($log[0] instanceof LoggerInterface) { + $this->loggedErrors |= $type; + } else { + throw new \InvalidArgumentException('Invalid logger provided'); + } + $this->loggers[$type] = $log + $prev[$type]; + + if ($this->bootstrappingLogger && $prev[$type][0] === $this->bootstrappingLogger) { + $flush[$type] = $type; + } + } + $this->reRegister($prevLogged | $this->thrownErrors); + + if ($flush) { + foreach ($this->bootstrappingLogger->cleanLogs() as $log) { + $type = $log[2]['exception'] instanceof \ErrorException ? $log[2]['exception']->getSeverity() : E_ERROR; + if (!isset($flush[$type])) { + $this->bootstrappingLogger->log($log[0], $log[1], $log[2]); + } elseif ($this->loggers[$type][0]) { + $this->loggers[$type][0]->log($this->loggers[$type][1], $log[1], $log[2]); + } + } + } + + return $prev; + } + + /** + * Sets a user exception handler. + * + * @param callable $handler A handler that will be called on Exception + * + * @return callable|null The previous exception handler + */ + public function setExceptionHandler(callable $handler = null) + { + $prev = $this->exceptionHandler; + $this->exceptionHandler = $handler; + + return $prev; + } + + /** + * Sets the PHP error levels that throw an exception when a PHP error occurs. + * + * @param int $levels A bit field of E_* constants for thrown errors + * @param bool $replace Replace or amend the previous value + * + * @return int The previous value + */ + public function throwAt($levels, $replace = false) + { + $prev = $this->thrownErrors; + $this->thrownErrors = ($levels | E_RECOVERABLE_ERROR | E_USER_ERROR) & ~E_USER_DEPRECATED & ~E_DEPRECATED; + if (!$replace) { + $this->thrownErrors |= $prev; + } + $this->reRegister($prev | $this->loggedErrors); + + return $prev; + } + + /** + * Sets the PHP error levels for which local variables are preserved. + * + * @param int $levels A bit field of E_* constants for scoped errors + * @param bool $replace Replace or amend the previous value + * + * @return int The previous value + */ + public function scopeAt($levels, $replace = false) + { + $prev = $this->scopedErrors; + $this->scopedErrors = (int) $levels; + if (!$replace) { + $this->scopedErrors |= $prev; + } + + return $prev; + } + + /** + * Sets the PHP error levels for which the stack trace is preserved. + * + * @param int $levels A bit field of E_* constants for traced errors + * @param bool $replace Replace or amend the previous value + * + * @return int The previous value + */ + public function traceAt($levels, $replace = false) + { + $prev = $this->tracedErrors; + $this->tracedErrors = (int) $levels; + if (!$replace) { + $this->tracedErrors |= $prev; + } + + return $prev; + } + + /** + * Sets the error levels where the @-operator is ignored. + * + * @param int $levels A bit field of E_* constants for screamed errors + * @param bool $replace Replace or amend the previous value + * + * @return int The previous value + */ + public function screamAt($levels, $replace = false) + { + $prev = $this->screamedErrors; + $this->screamedErrors = (int) $levels; + if (!$replace) { + $this->screamedErrors |= $prev; + } + + return $prev; + } + + /** + * Re-registers as a PHP error handler if levels changed. + */ + private function reRegister($prev) + { + if ($prev !== $this->thrownErrors | $this->loggedErrors) { + $handler = set_error_handler('var_dump'); + $handler = is_array($handler) ? $handler[0] : null; + restore_error_handler(); + if ($handler === $this) { + restore_error_handler(); + if ($this->isRoot) { + set_error_handler(array($this, 'handleError'), $this->thrownErrors | $this->loggedErrors); + } else { + set_error_handler(array($this, 'handleError')); + } + } + } + } + + /** + * Handles errors by filtering then logging them according to the configured bit fields. + * + * @param int $type One of the E_* constants + * @param string $message + * @param string $file + * @param int $line + * + * @return bool Returns false when no handling happens so that the PHP engine can handle the error itself + * + * @throws \ErrorException When $this->thrownErrors requests so + * + * @internal + */ + public function handleError($type, $message, $file, $line) + { + // Level is the current error reporting level to manage silent error. + // Strong errors are not authorized to be silenced. + $level = error_reporting() | E_RECOVERABLE_ERROR | E_USER_ERROR | E_DEPRECATED | E_USER_DEPRECATED; + $log = $this->loggedErrors & $type; + $throw = $this->thrownErrors & $type & $level; + $type &= $level | $this->screamedErrors; + + if (!$type || (!$log && !$throw)) { + return $type && $log; + } + $scope = $this->scopedErrors & $type; + + if (4 < $numArgs = func_num_args()) { + $context = $scope ? (func_get_arg(4) ?: array()) : array(); + $backtrace = 5 < $numArgs ? func_get_arg(5) : null; // defined on HHVM + } else { + $context = array(); + $backtrace = null; + } + + if (isset($context['GLOBALS']) && $scope) { + $e = $context; // Whatever the signature of the method, + unset($e['GLOBALS'], $context); // $context is always a reference in 5.3 + $context = $e; + } + + if (null !== $backtrace && $type & E_ERROR) { + // E_ERROR fatal errors are triggered on HHVM when + // hhvm.error_handling.call_user_handler_on_fatals=1 + // which is the way to get their backtrace. + $this->handleFatalError(compact('type', 'message', 'file', 'line', 'backtrace')); + + return true; + } + + $logMessage = $this->levels[$type].': '.$message; + + if (null !== self::$toStringException) { + $errorAsException = self::$toStringException; + self::$toStringException = null; + } elseif (!$throw && !($type & $level)) { + if (!isset(self::$silencedErrorCache[$id = $file.':'.$line])) { + $lightTrace = $this->tracedErrors & $type ? $this->cleanTrace(debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 3), $type, $file, $line, false) : array(); + $errorAsException = new SilencedErrorContext($type, $file, $line, $lightTrace); + } elseif (isset(self::$silencedErrorCache[$id][$message])) { + $lightTrace = null; + $errorAsException = self::$silencedErrorCache[$id][$message]; + ++$errorAsException->count; + } else { + $lightTrace = array(); + $errorAsException = null; + } + + if (100 < ++self::$silencedErrorCount) { + self::$silencedErrorCache = $lightTrace = array(); + self::$silencedErrorCount = 1; + } + if ($errorAsException) { + self::$silencedErrorCache[$id][$message] = $errorAsException; + } + if (null === $lightTrace) { + return; + } + } else { + if ($scope) { + $errorAsException = new ContextErrorException($logMessage, 0, $type, $file, $line, $context); + } else { + $errorAsException = new \ErrorException($logMessage, 0, $type, $file, $line); + } + + // Clean the trace by removing function arguments and the first frames added by the error handler itself. + if ($throw || $this->tracedErrors & $type) { + $backtrace = $backtrace ?: $errorAsException->getTrace(); + $lightTrace = $this->cleanTrace($backtrace, $type, $file, $line, $throw); + $this->traceReflector->setValue($errorAsException, $lightTrace); + } else { + $this->traceReflector->setValue($errorAsException, array()); + } + } + + if ($throw) { + if (E_USER_ERROR & $type) { + for ($i = 1; isset($backtrace[$i]); ++$i) { + if (isset($backtrace[$i]['function'], $backtrace[$i]['type'], $backtrace[$i - 1]['function']) + && '__toString' === $backtrace[$i]['function'] + && '->' === $backtrace[$i]['type'] + && !isset($backtrace[$i - 1]['class']) + && ('trigger_error' === $backtrace[$i - 1]['function'] || 'user_error' === $backtrace[$i - 1]['function']) + ) { + // Here, we know trigger_error() has been called from __toString(). + // HHVM is fine with throwing from __toString() but PHP triggers a fatal error instead. + // A small convention allows working around the limitation: + // given a caught $e exception in __toString(), quitting the method with + // `return trigger_error($e, E_USER_ERROR);` allows this error handler + // to make $e get through the __toString() barrier. + + foreach ($context as $e) { + if (($e instanceof \Exception || $e instanceof \Throwable) && $e->__toString() === $message) { + if (1 === $i) { + // On HHVM + $errorAsException = $e; + break; + } + self::$toStringException = $e; + + return true; + } + } + + if (1 < $i) { + // On PHP (not on HHVM), display the original error message instead of the default one. + $this->handleException($errorAsException); + + // Stop the process by giving back the error to the native handler. + return false; + } + } + } + } + + throw $errorAsException; + } + + if ($this->isRecursive) { + $log = 0; + } elseif (self::$stackedErrorLevels) { + self::$stackedErrors[] = array( + $this->loggers[$type][0], + ($type & $level) ? $this->loggers[$type][1] : LogLevel::DEBUG, + $logMessage, + $errorAsException ? array('exception' => $errorAsException) : array(), + ); + } else { + try { + $this->isRecursive = true; + $level = ($type & $level) ? $this->loggers[$type][1] : LogLevel::DEBUG; + $this->loggers[$type][0]->log($level, $logMessage, $errorAsException ? array('exception' => $errorAsException) : array()); + } finally { + $this->isRecursive = false; + } + } + + return $type && $log; + } + + /** + * Handles an exception by logging then forwarding it to another handler. + * + * @param \Exception|\Throwable $exception An exception to handle + * @param array $error An array as returned by error_get_last() + * + * @internal + */ + public function handleException($exception, array $error = null) + { + if (null === $error) { + self::$exitCode = 255; + } + if (!$exception instanceof \Exception) { + $exception = new FatalThrowableError($exception); + } + $type = $exception instanceof FatalErrorException ? $exception->getSeverity() : E_ERROR; + $handlerException = null; + + if (($this->loggedErrors & $type) || $exception instanceof FatalThrowableError) { + if ($exception instanceof FatalErrorException) { + if ($exception instanceof FatalThrowableError) { + $error = array( + 'type' => $type, + 'message' => $message = $exception->getMessage(), + 'file' => $exception->getFile(), + 'line' => $exception->getLine(), + ); + } else { + $message = 'Fatal '.$exception->getMessage(); + } + } elseif ($exception instanceof \ErrorException) { + $message = 'Uncaught '.$exception->getMessage(); + } else { + $message = 'Uncaught Exception: '.$exception->getMessage(); + } + } + if ($this->loggedErrors & $type) { + try { + $this->loggers[$type][0]->log($this->loggers[$type][1], $message, array('exception' => $exception)); + } catch (\Exception $handlerException) { + } catch (\Throwable $handlerException) { + } + } + if ($exception instanceof FatalErrorException && !$exception instanceof OutOfMemoryException && $error) { + foreach ($this->getFatalErrorHandlers() as $handler) { + if ($e = $handler->handleError($error, $exception)) { + $exception = $e; + break; + } + } + } + try { + if (null !== $this->exceptionHandler) { + return \call_user_func($this->exceptionHandler, $exception); + } + $handlerException = $handlerException ?: $exception; + } catch (\Exception $handlerException) { + } catch (\Throwable $handlerException) { + } + $this->exceptionHandler = null; + if ($exception === $handlerException) { + self::$reservedMemory = null; // Disable the fatal error handler + throw $exception; // Give back $exception to the native handler + } + $this->handleException($handlerException); + } + + /** + * Shutdown registered function for handling PHP fatal errors. + * + * @param array $error An array as returned by error_get_last() + * + * @internal + */ + public static function handleFatalError(array $error = null) + { + if (null === self::$reservedMemory) { + return; + } + + $handler = self::$reservedMemory = null; + $handlers = array(); + $previousHandler = null; + $sameHandlerLimit = 10; + + while (!is_array($handler) || !$handler[0] instanceof self) { + $handler = set_exception_handler('var_dump'); + restore_exception_handler(); + + if (!$handler) { + break; + } + restore_exception_handler(); + + if ($handler !== $previousHandler) { + array_unshift($handlers, $handler); + $previousHandler = $handler; + } elseif (0 === --$sameHandlerLimit) { + $handler = null; + break; + } + } + foreach ($handlers as $h) { + set_exception_handler($h); + } + if (!$handler) { + return; + } + if ($handler !== $h) { + $handler[0]->setExceptionHandler($h); + } + $handler = $handler[0]; + $handlers = array(); + + if ($exit = null === $error) { + $error = error_get_last(); + } + + try { + while (self::$stackedErrorLevels) { + static::unstackErrors(); + } + } catch (\Exception $exception) { + // Handled below + } catch (\Throwable $exception) { + // Handled below + } + + if ($error && $error['type'] &= E_PARSE | E_ERROR | E_CORE_ERROR | E_COMPILE_ERROR) { + // Let's not throw anymore but keep logging + $handler->throwAt(0, true); + $trace = isset($error['backtrace']) ? $error['backtrace'] : null; + + if (0 === strpos($error['message'], 'Allowed memory') || 0 === strpos($error['message'], 'Out of memory')) { + $exception = new OutOfMemoryException($handler->levels[$error['type']].': '.$error['message'], 0, $error['type'], $error['file'], $error['line'], 2, false, $trace); + } else { + $exception = new FatalErrorException($handler->levels[$error['type']].': '.$error['message'], 0, $error['type'], $error['file'], $error['line'], 2, true, $trace); + } + } + + try { + if (isset($exception)) { + self::$exitCode = 255; + $handler->handleException($exception, $error); + } + } catch (FatalErrorException $e) { + // Ignore this re-throw + } + + if ($exit && self::$exitCode) { + $exitCode = self::$exitCode; + register_shutdown_function('register_shutdown_function', function () use ($exitCode) { exit($exitCode); }); + } + } + + /** + * Configures the error handler for delayed handling. + * Ensures also that non-catchable fatal errors are never silenced. + * + * As shown by http://bugs.php.net/42098 and http://bugs.php.net/60724 + * PHP has a compile stage where it behaves unusually. To workaround it, + * we plug an error handler that only stacks errors for later. + * + * The most important feature of this is to prevent + * autoloading until unstackErrors() is called. + * + * @deprecated since version 3.4, to be removed in 4.0. + */ + public static function stackErrors() + { + @trigger_error('Support for stacking errors is deprecated since Symfony 3.4 and will be removed in 4.0.', E_USER_DEPRECATED); + + self::$stackedErrorLevels[] = error_reporting(error_reporting() | E_PARSE | E_ERROR | E_CORE_ERROR | E_COMPILE_ERROR); + } + + /** + * Unstacks stacked errors and forwards to the logger. + * + * @deprecated since version 3.4, to be removed in 4.0. + */ + public static function unstackErrors() + { + @trigger_error('Support for unstacking errors is deprecated since Symfony 3.4 and will be removed in 4.0.', E_USER_DEPRECATED); + + $level = array_pop(self::$stackedErrorLevels); + + if (null !== $level) { + $errorReportingLevel = error_reporting($level); + if ($errorReportingLevel !== ($level | E_PARSE | E_ERROR | E_CORE_ERROR | E_COMPILE_ERROR)) { + // If the user changed the error level, do not overwrite it + error_reporting($errorReportingLevel); + } + } + + if (empty(self::$stackedErrorLevels)) { + $errors = self::$stackedErrors; + self::$stackedErrors = array(); + + foreach ($errors as $error) { + $error[0]->log($error[1], $error[2], $error[3]); + } + } + } + + /** + * Gets the fatal error handlers. + * + * Override this method if you want to define more fatal error handlers. + * + * @return FatalErrorHandlerInterface[] An array of FatalErrorHandlerInterface + */ + protected function getFatalErrorHandlers() + { + return array( + new UndefinedFunctionFatalErrorHandler(), + new UndefinedMethodFatalErrorHandler(), + new ClassNotFoundFatalErrorHandler(), + ); + } + + private function cleanTrace($backtrace, $type, $file, $line, $throw) + { + $lightTrace = $backtrace; + + for ($i = 0; isset($backtrace[$i]); ++$i) { + if (isset($backtrace[$i]['file'], $backtrace[$i]['line']) && $backtrace[$i]['line'] === $line && $backtrace[$i]['file'] === $file) { + $lightTrace = array_slice($lightTrace, 1 + $i); + break; + } + } + if (!($throw || $this->scopedErrors & $type)) { + for ($i = 0; isset($lightTrace[$i]); ++$i) { + unset($lightTrace[$i]['args'], $lightTrace[$i]['object']); + } + } + + return $lightTrace; + } +} diff --git a/lib/silex/vendor/symfony/debug/Exception/ClassNotFoundException.php b/lib/silex/vendor/symfony/debug/Exception/ClassNotFoundException.php new file mode 100644 index 000000000..b91bf4663 --- /dev/null +++ b/lib/silex/vendor/symfony/debug/Exception/ClassNotFoundException.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\Debug\Exception; + +/** + * Class (or Trait or Interface) Not Found Exception. + * + * @author Konstanton Myakshin + */ +class ClassNotFoundException extends FatalErrorException +{ + public function __construct($message, \ErrorException $previous) + { + parent::__construct( + $message, + $previous->getCode(), + $previous->getSeverity(), + $previous->getFile(), + $previous->getLine(), + $previous->getPrevious() + ); + $this->setTrace($previous->getTrace()); + } +} diff --git a/lib/silex/vendor/symfony/debug/Exception/ContextErrorException.php b/lib/silex/vendor/symfony/debug/Exception/ContextErrorException.php new file mode 100644 index 000000000..554139da3 --- /dev/null +++ b/lib/silex/vendor/symfony/debug/Exception/ContextErrorException.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\Debug\Exception; + +/** + * Error Exception with Variable Context. + * + * @author Christian Sciberras + * + * @deprecated since version 3.3. Instead, \ErrorException will be used directly in 4.0. + */ +class ContextErrorException extends \ErrorException +{ + private $context = array(); + + public function __construct($message, $code, $severity, $filename, $lineno, $context = array()) + { + parent::__construct($message, $code, $severity, $filename, $lineno); + $this->context = $context; + } + + /** + * @return array Array of variables that existed when the exception occurred + */ + public function getContext() + { + @trigger_error(sprintf('The %s class is deprecated since Symfony 3.3 and will be removed in 4.0.', __CLASS__), E_USER_DEPRECATED); + + return $this->context; + } +} diff --git a/lib/silex/vendor/symfony/debug/Exception/FatalErrorException.php b/lib/silex/vendor/symfony/debug/Exception/FatalErrorException.php new file mode 100644 index 000000000..f24a54e77 --- /dev/null +++ b/lib/silex/vendor/symfony/debug/Exception/FatalErrorException.php @@ -0,0 +1,82 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Debug\Exception; + +/** + * Fatal Error Exception. + * + * @author Konstanton Myakshin + */ +class FatalErrorException extends \ErrorException +{ + public function __construct($message, $code, $severity, $filename, $lineno, $traceOffset = null, $traceArgs = true, array $trace = null) + { + parent::__construct($message, $code, $severity, $filename, $lineno); + + if (null !== $trace) { + if (!$traceArgs) { + foreach ($trace as &$frame) { + unset($frame['args'], $frame['this'], $frame); + } + } + + $this->setTrace($trace); + } elseif (null !== $traceOffset) { + if (function_exists('xdebug_get_function_stack')) { + $trace = xdebug_get_function_stack(); + if (0 < $traceOffset) { + array_splice($trace, -$traceOffset); + } + + foreach ($trace as &$frame) { + if (!isset($frame['type'])) { + // XDebug pre 2.1.1 doesn't currently set the call type key http://bugs.xdebug.org/view.php?id=695 + if (isset($frame['class'])) { + $frame['type'] = '::'; + } + } elseif ('dynamic' === $frame['type']) { + $frame['type'] = '->'; + } elseif ('static' === $frame['type']) { + $frame['type'] = '::'; + } + + // XDebug also has a different name for the parameters array + if (!$traceArgs) { + unset($frame['params'], $frame['args']); + } elseif (isset($frame['params']) && !isset($frame['args'])) { + $frame['args'] = $frame['params']; + unset($frame['params']); + } + } + + unset($frame); + $trace = array_reverse($trace); + } elseif (function_exists('symfony_debug_backtrace')) { + $trace = symfony_debug_backtrace(); + if (0 < $traceOffset) { + array_splice($trace, 0, $traceOffset); + } + } else { + $trace = array(); + } + + $this->setTrace($trace); + } + } + + protected function setTrace($trace) + { + $traceReflector = new \ReflectionProperty('Exception', 'trace'); + $traceReflector->setAccessible(true); + $traceReflector->setValue($this, $trace); + } +} diff --git a/lib/silex/vendor/symfony/debug/Exception/FatalThrowableError.php b/lib/silex/vendor/symfony/debug/Exception/FatalThrowableError.php new file mode 100644 index 000000000..34f43b17b --- /dev/null +++ b/lib/silex/vendor/symfony/debug/Exception/FatalThrowableError.php @@ -0,0 +1,44 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Debug\Exception; + +/** + * Fatal Throwable Error. + * + * @author Nicolas Grekas + */ +class FatalThrowableError extends FatalErrorException +{ + public function __construct(\Throwable $e) + { + if ($e instanceof \ParseError) { + $message = 'Parse error: '.$e->getMessage(); + $severity = E_PARSE; + } elseif ($e instanceof \TypeError) { + $message = 'Type error: '.$e->getMessage(); + $severity = E_RECOVERABLE_ERROR; + } else { + $message = $e->getMessage(); + $severity = E_ERROR; + } + + \ErrorException::__construct( + $message, + $e->getCode(), + $severity, + $e->getFile(), + $e->getLine() + ); + + $this->setTrace($e->getTrace()); + } +} diff --git a/lib/silex/vendor/symfony/debug/Exception/FlattenException.php b/lib/silex/vendor/symfony/debug/Exception/FlattenException.php new file mode 100644 index 000000000..24679dcaa --- /dev/null +++ b/lib/silex/vendor/symfony/debug/Exception/FlattenException.php @@ -0,0 +1,263 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Debug\Exception; + +use Symfony\Component\HttpFoundation\Exception\RequestExceptionInterface; +use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface; + +/** + * FlattenException wraps a PHP Exception to be able to serialize it. + * + * Basically, this class removes all objects from the trace. + * + * @author Fabien Potencier + */ +class FlattenException +{ + private $message; + private $code; + private $previous; + private $trace; + private $class; + private $statusCode; + private $headers; + private $file; + private $line; + + public static function create(\Exception $exception, $statusCode = null, array $headers = array()) + { + $e = new static(); + $e->setMessage($exception->getMessage()); + $e->setCode($exception->getCode()); + + if ($exception instanceof HttpExceptionInterface) { + $statusCode = $exception->getStatusCode(); + $headers = array_merge($headers, $exception->getHeaders()); + } elseif ($exception instanceof RequestExceptionInterface) { + $statusCode = 400; + } + + if (null === $statusCode) { + $statusCode = 500; + } + + $e->setStatusCode($statusCode); + $e->setHeaders($headers); + $e->setTraceFromException($exception); + $e->setClass(get_class($exception)); + $e->setFile($exception->getFile()); + $e->setLine($exception->getLine()); + + $previous = $exception->getPrevious(); + + if ($previous instanceof \Exception) { + $e->setPrevious(static::create($previous)); + } elseif ($previous instanceof \Throwable) { + $e->setPrevious(static::create(new FatalThrowableError($previous))); + } + + return $e; + } + + public function toArray() + { + $exceptions = array(); + foreach (array_merge(array($this), $this->getAllPrevious()) as $exception) { + $exceptions[] = array( + 'message' => $exception->getMessage(), + 'class' => $exception->getClass(), + 'trace' => $exception->getTrace(), + ); + } + + return $exceptions; + } + + public function getStatusCode() + { + return $this->statusCode; + } + + public function setStatusCode($code) + { + $this->statusCode = $code; + } + + public function getHeaders() + { + return $this->headers; + } + + public function setHeaders(array $headers) + { + $this->headers = $headers; + } + + public function getClass() + { + return $this->class; + } + + public function setClass($class) + { + $this->class = $class; + } + + public function getFile() + { + return $this->file; + } + + public function setFile($file) + { + $this->file = $file; + } + + public function getLine() + { + return $this->line; + } + + public function setLine($line) + { + $this->line = $line; + } + + public function getMessage() + { + return $this->message; + } + + public function setMessage($message) + { + $this->message = $message; + } + + public function getCode() + { + return $this->code; + } + + public function setCode($code) + { + $this->code = $code; + } + + public function getPrevious() + { + return $this->previous; + } + + public function setPrevious(FlattenException $previous) + { + $this->previous = $previous; + } + + public function getAllPrevious() + { + $exceptions = array(); + $e = $this; + while ($e = $e->getPrevious()) { + $exceptions[] = $e; + } + + return $exceptions; + } + + public function getTrace() + { + return $this->trace; + } + + public function setTraceFromException(\Exception $exception) + { + $this->setTrace($exception->getTrace(), $exception->getFile(), $exception->getLine()); + } + + public function setTrace($trace, $file, $line) + { + $this->trace = array(); + $this->trace[] = array( + 'namespace' => '', + 'short_class' => '', + 'class' => '', + 'type' => '', + 'function' => '', + 'file' => $file, + 'line' => $line, + 'args' => array(), + ); + foreach ($trace as $entry) { + $class = ''; + $namespace = ''; + if (isset($entry['class'])) { + $parts = explode('\\', $entry['class']); + $class = array_pop($parts); + $namespace = implode('\\', $parts); + } + + $this->trace[] = array( + 'namespace' => $namespace, + 'short_class' => $class, + 'class' => isset($entry['class']) ? $entry['class'] : '', + 'type' => isset($entry['type']) ? $entry['type'] : '', + 'function' => isset($entry['function']) ? $entry['function'] : null, + 'file' => isset($entry['file']) ? $entry['file'] : null, + 'line' => isset($entry['line']) ? $entry['line'] : null, + 'args' => isset($entry['args']) ? $this->flattenArgs($entry['args']) : array(), + ); + } + } + + private function flattenArgs($args, $level = 0, &$count = 0) + { + $result = array(); + foreach ($args as $key => $value) { + if (++$count > 1e4) { + return array('array', '*SKIPPED over 10000 entries*'); + } + if ($value instanceof \__PHP_Incomplete_Class) { + // is_object() returns false on PHP<=7.1 + $result[$key] = array('incomplete-object', $this->getClassNameFromIncomplete($value)); + } elseif (is_object($value)) { + $result[$key] = array('object', get_class($value)); + } elseif (is_array($value)) { + if ($level > 10) { + $result[$key] = array('array', '*DEEP NESTED ARRAY*'); + } else { + $result[$key] = array('array', $this->flattenArgs($value, $level + 1, $count)); + } + } elseif (null === $value) { + $result[$key] = array('null', null); + } elseif (is_bool($value)) { + $result[$key] = array('boolean', $value); + } elseif (is_int($value)) { + $result[$key] = array('integer', $value); + } elseif (is_float($value)) { + $result[$key] = array('float', $value); + } elseif (is_resource($value)) { + $result[$key] = array('resource', get_resource_type($value)); + } else { + $result[$key] = array('string', (string) $value); + } + } + + return $result; + } + + private function getClassNameFromIncomplete(\__PHP_Incomplete_Class $value) + { + $array = new \ArrayObject($value); + + return $array['__PHP_Incomplete_Class_Name']; + } +} diff --git a/lib/silex/vendor/symfony/debug/Exception/OutOfMemoryException.php b/lib/silex/vendor/symfony/debug/Exception/OutOfMemoryException.php new file mode 100644 index 000000000..fec197983 --- /dev/null +++ b/lib/silex/vendor/symfony/debug/Exception/OutOfMemoryException.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\Debug\Exception; + +/** + * Out of memory exception. + * + * @author Nicolas Grekas + */ +class OutOfMemoryException extends FatalErrorException +{ +} diff --git a/lib/silex/vendor/symfony/debug/Exception/SilencedErrorContext.php b/lib/silex/vendor/symfony/debug/Exception/SilencedErrorContext.php new file mode 100644 index 000000000..4be83491b --- /dev/null +++ b/lib/silex/vendor/symfony/debug/Exception/SilencedErrorContext.php @@ -0,0 +1,67 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Debug\Exception; + +/** + * Data Object that represents a Silenced Error. + * + * @author Grégoire Pineau + */ +class SilencedErrorContext implements \JsonSerializable +{ + public $count = 1; + + private $severity; + private $file; + private $line; + private $trace; + + public function __construct($severity, $file, $line, array $trace = array(), $count = 1) + { + $this->severity = $severity; + $this->file = $file; + $this->line = $line; + $this->trace = $trace; + $this->count = $count; + } + + public function getSeverity() + { + return $this->severity; + } + + public function getFile() + { + return $this->file; + } + + public function getLine() + { + return $this->line; + } + + public function getTrace() + { + return $this->trace; + } + + public function JsonSerialize() + { + return array( + 'severity' => $this->severity, + 'file' => $this->file, + 'line' => $this->line, + 'trace' => $this->trace, + 'count' => $this->count, + ); + } +} diff --git a/lib/silex/vendor/symfony/debug/Exception/UndefinedFunctionException.php b/lib/silex/vendor/symfony/debug/Exception/UndefinedFunctionException.php new file mode 100644 index 000000000..a66ae2a38 --- /dev/null +++ b/lib/silex/vendor/symfony/debug/Exception/UndefinedFunctionException.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\Debug\Exception; + +/** + * Undefined Function Exception. + * + * @author Konstanton Myakshin + */ +class UndefinedFunctionException extends FatalErrorException +{ + public function __construct($message, \ErrorException $previous) + { + parent::__construct( + $message, + $previous->getCode(), + $previous->getSeverity(), + $previous->getFile(), + $previous->getLine(), + $previous->getPrevious() + ); + $this->setTrace($previous->getTrace()); + } +} diff --git a/lib/silex/vendor/symfony/debug/Exception/UndefinedMethodException.php b/lib/silex/vendor/symfony/debug/Exception/UndefinedMethodException.php new file mode 100644 index 000000000..350dc3187 --- /dev/null +++ b/lib/silex/vendor/symfony/debug/Exception/UndefinedMethodException.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\Debug\Exception; + +/** + * Undefined Method Exception. + * + * @author Grégoire Pineau + */ +class UndefinedMethodException extends FatalErrorException +{ + public function __construct($message, \ErrorException $previous) + { + parent::__construct( + $message, + $previous->getCode(), + $previous->getSeverity(), + $previous->getFile(), + $previous->getLine(), + $previous->getPrevious() + ); + $this->setTrace($previous->getTrace()); + } +} diff --git a/lib/silex/vendor/symfony/debug/ExceptionHandler.php b/lib/silex/vendor/symfony/debug/ExceptionHandler.php new file mode 100644 index 000000000..97470cb6b --- /dev/null +++ b/lib/silex/vendor/symfony/debug/ExceptionHandler.php @@ -0,0 +1,410 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Debug; + +use Symfony\Component\Debug\Exception\FlattenException; +use Symfony\Component\Debug\Exception\OutOfMemoryException; +use Symfony\Component\HttpKernel\Debug\FileLinkFormatter; + +/** + * ExceptionHandler converts an exception to a Response object. + * + * It is mostly useful in debug mode to replace the default PHP/XDebug + * output with something prettier and more useful. + * + * As this class is mainly used during Kernel boot, where nothing is yet + * available, the Response content is always HTML. + * + * @author Fabien Potencier + * @author Nicolas Grekas + */ +class ExceptionHandler +{ + private $debug; + private $charset; + private $handler; + private $caughtBuffer; + private $caughtLength; + private $fileLinkFormat; + + public function __construct($debug = true, $charset = null, $fileLinkFormat = null) + { + $this->debug = $debug; + $this->charset = $charset ?: ini_get('default_charset') ?: 'UTF-8'; + $this->fileLinkFormat = $fileLinkFormat ?: ini_get('xdebug.file_link_format') ?: get_cfg_var('xdebug.file_link_format'); + } + + /** + * Registers the exception handler. + * + * @param bool $debug Enable/disable debug mode, where the stack trace is displayed + * @param string|null $charset The charset used by exception messages + * @param string|null $fileLinkFormat The IDE link template + * + * @return static + */ + public static function register($debug = true, $charset = null, $fileLinkFormat = null) + { + $handler = new static($debug, $charset, $fileLinkFormat); + + $prev = set_exception_handler(array($handler, 'handle')); + if (is_array($prev) && $prev[0] instanceof ErrorHandler) { + restore_exception_handler(); + $prev[0]->setExceptionHandler(array($handler, 'handle')); + } + + return $handler; + } + + /** + * Sets a user exception handler. + * + * @param callable $handler An handler that will be called on Exception + * + * @return callable|null The previous exception handler if any + */ + public function setHandler(callable $handler = null) + { + $old = $this->handler; + $this->handler = $handler; + + return $old; + } + + /** + * Sets the format for links to source files. + * + * @param string|FileLinkFormatter $fileLinkFormat The format for links to source files + * + * @return string The previous file link format + */ + public function setFileLinkFormat($fileLinkFormat) + { + $old = $this->fileLinkFormat; + $this->fileLinkFormat = $fileLinkFormat; + + return $old; + } + + /** + * Sends a response for the given Exception. + * + * To be as fail-safe as possible, the exception is first handled + * by our simple exception handler, then by the user exception handler. + * The latter takes precedence and any output from the former is cancelled, + * if and only if nothing bad happens in this handling path. + */ + public function handle(\Exception $exception) + { + if (null === $this->handler || $exception instanceof OutOfMemoryException) { + $this->sendPhpResponse($exception); + + return; + } + + $caughtLength = $this->caughtLength = 0; + + ob_start(function ($buffer) { + $this->caughtBuffer = $buffer; + + return ''; + }); + + $this->sendPhpResponse($exception); + while (null === $this->caughtBuffer && ob_end_flush()) { + // Empty loop, everything is in the condition + } + if (isset($this->caughtBuffer[0])) { + ob_start(function ($buffer) { + if ($this->caughtLength) { + // use substr_replace() instead of substr() for mbstring overloading resistance + $cleanBuffer = substr_replace($buffer, '', 0, $this->caughtLength); + if (isset($cleanBuffer[0])) { + $buffer = $cleanBuffer; + } + } + + return $buffer; + }); + + echo $this->caughtBuffer; + $caughtLength = ob_get_length(); + } + $this->caughtBuffer = null; + + try { + call_user_func($this->handler, $exception); + $this->caughtLength = $caughtLength; + } catch (\Exception $e) { + if (!$caughtLength) { + // All handlers failed. Let PHP handle that now. + throw $exception; + } + } + } + + /** + * Sends the error associated with the given Exception as a plain PHP response. + * + * This method uses plain PHP functions like header() and echo to output + * the response. + * + * @param \Exception|FlattenException $exception An \Exception or FlattenException instance + */ + public function sendPhpResponse($exception) + { + if (!$exception instanceof FlattenException) { + $exception = FlattenException::create($exception); + } + + if (!headers_sent()) { + header(sprintf('HTTP/1.0 %s', $exception->getStatusCode())); + foreach ($exception->getHeaders() as $name => $value) { + header($name.': '.$value, false); + } + header('Content-Type: text/html; charset='.$this->charset); + } + + echo $this->decorate($this->getContent($exception), $this->getStylesheet($exception)); + } + + /** + * Gets the full HTML content associated with the given exception. + * + * @param \Exception|FlattenException $exception An \Exception or FlattenException instance + * + * @return string The HTML content as a string + */ + public function getHtml($exception) + { + if (!$exception instanceof FlattenException) { + $exception = FlattenException::create($exception); + } + + return $this->decorate($this->getContent($exception), $this->getStylesheet($exception)); + } + + /** + * Gets the HTML content associated with the given exception. + * + * @return string The content as a string + */ + public function getContent(FlattenException $exception) + { + switch ($exception->getStatusCode()) { + case 404: + $title = 'Sorry, the page you are looking for could not be found.'; + break; + default: + $title = 'Whoops, looks like something went wrong.'; + } + + $content = ''; + if ($this->debug) { + try { + $count = count($exception->getAllPrevious()); + $total = $count + 1; + foreach ($exception->toArray() as $position => $e) { + $ind = $count - $position + 1; + $class = $this->formatClass($e['class']); + $message = nl2br($this->escapeHtml($e['message'])); + $content .= sprintf(<<<'EOF' +
+ + + +EOF + , $ind, $total, $class, $message); + foreach ($e['trace'] as $trace) { + $content .= '\n"; + } + + $content .= "\n
+

+ (%d/%d) + %s +

+

%s

+
'; + if ($trace['function']) { + $content .= sprintf('at %s%s%s(%s)', $this->formatClass($trace['class']), $trace['type'], $trace['function'], $this->formatArgs($trace['args'])); + } + if (isset($trace['file']) && isset($trace['line'])) { + $content .= $this->formatPath($trace['file'], $trace['line']); + } + $content .= "
\n
\n"; + } + } catch (\Exception $e) { + // something nasty happened and we cannot throw an exception anymore + if ($this->debug) { + $title = sprintf('Exception thrown when handling an exception (%s: %s)', get_class($e), $this->escapeHtml($e->getMessage())); + } else { + $title = 'Whoops, looks like something went wrong.'; + } + } + } + + $symfonyGhostImageContents = $this->getSymfonyGhostAsSvg(); + + return << +
+
+

$title

+
$symfonyGhostImageContents
+
+
+ + +
+ $content +
+EOF; + } + + /** + * Gets the stylesheet associated with the given exception. + * + * @return string The stylesheet as a string + */ + public function getStylesheet(FlattenException $exception) + { + return <<<'EOF' + body { background-color: #F9F9F9; color: #222; font: 14px/1.4 Helvetica, Arial, sans-serif; margin: 0; padding-bottom: 45px; } + + a { cursor: pointer; text-decoration: none; } + a:hover { text-decoration: underline; } + abbr[title] { border-bottom: none; cursor: help; text-decoration: none; } + + code, pre { font: 13px/1.5 Consolas, Monaco, Menlo, "Ubuntu Mono", "Liberation Mono", monospace; } + + table, tr, th, td { background: #FFF; border-collapse: collapse; vertical-align: top; } + table { background: #FFF; border: 1px solid #E0E0E0; box-shadow: 0px 0px 1px rgba(128, 128, 128, .2); margin: 1em 0; width: 100%; } + table th, table td { border: solid #E0E0E0; border-width: 1px 0; padding: 8px 10px; } + table th { background-color: #E0E0E0; font-weight: bold; text-align: left; } + + .hidden-xs-down { display: none; } + .block { display: block; } + .break-long-words { -ms-word-break: break-all; word-break: break-all; word-break: break-word; -webkit-hyphens: auto; -moz-hyphens: auto; hyphens: auto; } + .text-muted { color: #999; } + + .container { max-width: 1024px; margin: 0 auto; padding: 0 15px; } + .container::after { content: ""; display: table; clear: both; } + + .exception-summary { background: #B0413E; border-bottom: 2px solid rgba(0, 0, 0, 0.1); border-top: 1px solid rgba(0, 0, 0, .3); flex: 0 0 auto; margin-bottom: 30px; } + + .exception-message-wrapper { display: flex; align-items: center; min-height: 70px; } + .exception-message { flex-grow: 1; padding: 30px 0; } + .exception-message, .exception-message a { color: #FFF; font-size: 21px; font-weight: 400; margin: 0; } + .exception-message.long { font-size: 18px; } + .exception-message a { border-bottom: 1px solid rgba(255, 255, 255, 0.5); font-size: inherit; text-decoration: none; } + .exception-message a:hover { border-bottom-color: #ffffff; } + + .exception-illustration { flex-basis: 111px; flex-shrink: 0; height: 66px; margin-left: 15px; opacity: .7; } + + .trace + .trace { margin-top: 30px; } + .trace-head .trace-class { color: #222; font-size: 18px; font-weight: bold; line-height: 1.3; margin: 0; position: relative; } + + .trace-message { font-size: 14px; font-weight: normal; margin: .5em 0 0; } + + .trace-file-path, .trace-file-path a { color: #222; margin-top: 3px; font-size: 13px; } + .trace-class { color: #B0413E; } + .trace-type { padding: 0 2px; } + .trace-method { color: #B0413E; font-weight: bold; } + .trace-arguments { color: #777; font-weight: normal; padding-left: 2px; } + + @media (min-width: 575px) { + .hidden-xs-down { display: initial; } + } +EOF; + } + + private function decorate($content, $css) + { + return << + + + + + + + + $content + + +EOF; + } + + private function formatClass($class) + { + $parts = explode('\\', $class); + + return sprintf('%s', $class, array_pop($parts)); + } + + private function formatPath($path, $line) + { + $file = $this->escapeHtml(preg_match('#[^/\\\\]*+$#', $path, $file) ? $file[0] : $path); + $fmt = $this->fileLinkFormat; + + if ($fmt && $link = is_string($fmt) ? strtr($fmt, array('%f' => $path, '%l' => $line)) : $fmt->format($path, $line)) { + return sprintf('in %s (line %d)', $this->escapeHtml($link), $file, $line); + } + + return sprintf('in %s (line %d)', $this->escapeHtml($path), $file, $line); + } + + /** + * Formats an array as a string. + * + * @param array $args The argument array + * + * @return string + */ + private function formatArgs(array $args) + { + $result = array(); + foreach ($args as $key => $item) { + if ('object' === $item[0]) { + $formattedValue = sprintf('object(%s)', $this->formatClass($item[1])); + } elseif ('array' === $item[0]) { + $formattedValue = sprintf('array(%s)', is_array($item[1]) ? $this->formatArgs($item[1]) : $item[1]); + } elseif ('null' === $item[0]) { + $formattedValue = 'null'; + } elseif ('boolean' === $item[0]) { + $formattedValue = ''.strtolower(var_export($item[1], true)).''; + } elseif ('resource' === $item[0]) { + $formattedValue = 'resource'; + } else { + $formattedValue = str_replace("\n", '', $this->escapeHtml(var_export($item[1], true))); + } + + $result[] = is_int($key) ? $formattedValue : sprintf("'%s' => %s", $this->escapeHtml($key), $formattedValue); + } + + return implode(', ', $result); + } + + /** + * HTML-encodes a string. + */ + private function escapeHtml($str) + { + return htmlspecialchars($str, ENT_COMPAT | ENT_SUBSTITUTE, $this->charset); + } + + private function getSymfonyGhostAsSvg() + { + return ''; + } +} diff --git a/lib/silex/vendor/symfony/debug/FatalErrorHandler/ClassNotFoundFatalErrorHandler.php b/lib/silex/vendor/symfony/debug/FatalErrorHandler/ClassNotFoundFatalErrorHandler.php new file mode 100644 index 000000000..32ba9a09c --- /dev/null +++ b/lib/silex/vendor/symfony/debug/FatalErrorHandler/ClassNotFoundFatalErrorHandler.php @@ -0,0 +1,206 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Debug\FatalErrorHandler; + +use Symfony\Component\Debug\Exception\ClassNotFoundException; +use Symfony\Component\Debug\Exception\FatalErrorException; +use Symfony\Component\Debug\DebugClassLoader; +use Composer\Autoload\ClassLoader as ComposerClassLoader; +use Symfony\Component\ClassLoader\ClassLoader as SymfonyClassLoader; + +/** + * ErrorHandler for classes that do not exist. + * + * @author Fabien Potencier + */ +class ClassNotFoundFatalErrorHandler implements FatalErrorHandlerInterface +{ + /** + * {@inheritdoc} + */ + public function handleError(array $error, FatalErrorException $exception) + { + $messageLen = strlen($error['message']); + $notFoundSuffix = '\' not found'; + $notFoundSuffixLen = strlen($notFoundSuffix); + if ($notFoundSuffixLen > $messageLen) { + return; + } + + if (0 !== substr_compare($error['message'], $notFoundSuffix, -$notFoundSuffixLen)) { + return; + } + + foreach (array('class', 'interface', 'trait') as $typeName) { + $prefix = ucfirst($typeName).' \''; + $prefixLen = strlen($prefix); + if (0 !== strpos($error['message'], $prefix)) { + continue; + } + + $fullyQualifiedClassName = substr($error['message'], $prefixLen, -$notFoundSuffixLen); + if (false !== $namespaceSeparatorIndex = strrpos($fullyQualifiedClassName, '\\')) { + $className = substr($fullyQualifiedClassName, $namespaceSeparatorIndex + 1); + $namespacePrefix = substr($fullyQualifiedClassName, 0, $namespaceSeparatorIndex); + $message = sprintf('Attempted to load %s "%s" from namespace "%s".', $typeName, $className, $namespacePrefix); + $tail = ' for another namespace?'; + } else { + $className = $fullyQualifiedClassName; + $message = sprintf('Attempted to load %s "%s" from the global namespace.', $typeName, $className); + $tail = '?'; + } + + if ($candidates = $this->getClassCandidates($className)) { + $tail = array_pop($candidates).'"?'; + if ($candidates) { + $tail = ' for e.g. "'.implode('", "', $candidates).'" or "'.$tail; + } else { + $tail = ' for "'.$tail; + } + } + $message .= "\nDid you forget a \"use\" statement".$tail; + + return new ClassNotFoundException($message, $exception); + } + } + + /** + * Tries to guess the full namespace for a given class name. + * + * By default, it looks for PSR-0 and PSR-4 classes registered via a Symfony or a Composer + * autoloader (that should cover all common cases). + * + * @param string $class A class name (without its namespace) + * + * @return array An array of possible fully qualified class names + */ + private function getClassCandidates($class) + { + if (!is_array($functions = spl_autoload_functions())) { + return array(); + } + + // find Symfony and Composer autoloaders + $classes = array(); + + foreach ($functions as $function) { + if (!is_array($function)) { + continue; + } + // get class loaders wrapped by DebugClassLoader + if ($function[0] instanceof DebugClassLoader) { + $function = $function[0]->getClassLoader(); + + if (!is_array($function)) { + continue; + } + } + + if ($function[0] instanceof ComposerClassLoader || $function[0] instanceof SymfonyClassLoader) { + foreach ($function[0]->getPrefixes() as $prefix => $paths) { + foreach ($paths as $path) { + $classes = array_merge($classes, $this->findClassInPath($path, $class, $prefix)); + } + } + } + if ($function[0] instanceof ComposerClassLoader) { + foreach ($function[0]->getPrefixesPsr4() as $prefix => $paths) { + foreach ($paths as $path) { + $classes = array_merge($classes, $this->findClassInPath($path, $class, $prefix)); + } + } + } + } + + return array_unique($classes); + } + + /** + * @param string $path + * @param string $class + * @param string $prefix + * + * @return array + */ + private function findClassInPath($path, $class, $prefix) + { + if (!$path = realpath($path.'/'.strtr($prefix, '\\_', '//')) ?: realpath($path.'/'.dirname(strtr($prefix, '\\_', '//'))) ?: realpath($path)) { + return array(); + } + + $classes = array(); + $filename = $class.'.php'; + foreach (new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($path, \RecursiveDirectoryIterator::SKIP_DOTS), \RecursiveIteratorIterator::LEAVES_ONLY) as $file) { + if ($filename == $file->getFileName() && $class = $this->convertFileToClass($path, $file->getPathName(), $prefix)) { + $classes[] = $class; + } + } + + return $classes; + } + + /** + * @param string $path + * @param string $file + * @param string $prefix + * + * @return string|null + */ + private function convertFileToClass($path, $file, $prefix) + { + $candidates = array( + // namespaced class + $namespacedClass = str_replace(array($path.DIRECTORY_SEPARATOR, '.php', '/'), array('', '', '\\'), $file), + // namespaced class (with target dir) + $prefix.$namespacedClass, + // namespaced class (with target dir and separator) + $prefix.'\\'.$namespacedClass, + // PEAR class + str_replace('\\', '_', $namespacedClass), + // PEAR class (with target dir) + str_replace('\\', '_', $prefix.$namespacedClass), + // PEAR class (with target dir and separator) + str_replace('\\', '_', $prefix.'\\'.$namespacedClass), + ); + + if ($prefix) { + $candidates = array_filter($candidates, function ($candidate) use ($prefix) { return 0 === strpos($candidate, $prefix); }); + } + + // We cannot use the autoloader here as most of them use require; but if the class + // is not found, the new autoloader call will require the file again leading to a + // "cannot redeclare class" error. + foreach ($candidates as $candidate) { + if ($this->classExists($candidate)) { + return $candidate; + } + } + + require_once $file; + + foreach ($candidates as $candidate) { + if ($this->classExists($candidate)) { + return $candidate; + } + } + } + + /** + * @param string $class + * + * @return bool + */ + private function classExists($class) + { + return class_exists($class, false) || interface_exists($class, false) || trait_exists($class, false); + } +} diff --git a/lib/silex/vendor/symfony/debug/FatalErrorHandler/FatalErrorHandlerInterface.php b/lib/silex/vendor/symfony/debug/FatalErrorHandler/FatalErrorHandlerInterface.php new file mode 100644 index 000000000..6b87eb30a --- /dev/null +++ b/lib/silex/vendor/symfony/debug/FatalErrorHandler/FatalErrorHandlerInterface.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\Debug\FatalErrorHandler; + +use Symfony\Component\Debug\Exception\FatalErrorException; + +/** + * Attempts to convert fatal errors to exceptions. + * + * @author Fabien Potencier + */ +interface FatalErrorHandlerInterface +{ + /** + * Attempts to convert an error into an exception. + * + * @param array $error An array as returned by error_get_last() + * @param FatalErrorException $exception A FatalErrorException instance + * + * @return FatalErrorException|null A FatalErrorException instance if the class is able to convert the error, null otherwise + */ + public function handleError(array $error, FatalErrorException $exception); +} diff --git a/lib/silex/vendor/symfony/debug/FatalErrorHandler/UndefinedFunctionFatalErrorHandler.php b/lib/silex/vendor/symfony/debug/FatalErrorHandler/UndefinedFunctionFatalErrorHandler.php new file mode 100644 index 000000000..c6f391a79 --- /dev/null +++ b/lib/silex/vendor/symfony/debug/FatalErrorHandler/UndefinedFunctionFatalErrorHandler.php @@ -0,0 +1,84 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Debug\FatalErrorHandler; + +use Symfony\Component\Debug\Exception\UndefinedFunctionException; +use Symfony\Component\Debug\Exception\FatalErrorException; + +/** + * ErrorHandler for undefined functions. + * + * @author Fabien Potencier + */ +class UndefinedFunctionFatalErrorHandler implements FatalErrorHandlerInterface +{ + /** + * {@inheritdoc} + */ + public function handleError(array $error, FatalErrorException $exception) + { + $messageLen = strlen($error['message']); + $notFoundSuffix = '()'; + $notFoundSuffixLen = strlen($notFoundSuffix); + if ($notFoundSuffixLen > $messageLen) { + return; + } + + if (0 !== substr_compare($error['message'], $notFoundSuffix, -$notFoundSuffixLen)) { + return; + } + + $prefix = 'Call to undefined function '; + $prefixLen = strlen($prefix); + if (0 !== strpos($error['message'], $prefix)) { + return; + } + + $fullyQualifiedFunctionName = substr($error['message'], $prefixLen, -$notFoundSuffixLen); + if (false !== $namespaceSeparatorIndex = strrpos($fullyQualifiedFunctionName, '\\')) { + $functionName = substr($fullyQualifiedFunctionName, $namespaceSeparatorIndex + 1); + $namespacePrefix = substr($fullyQualifiedFunctionName, 0, $namespaceSeparatorIndex); + $message = sprintf('Attempted to call function "%s" from namespace "%s".', $functionName, $namespacePrefix); + } else { + $functionName = $fullyQualifiedFunctionName; + $message = sprintf('Attempted to call function "%s" from the global namespace.', $functionName); + } + + $candidates = array(); + foreach (get_defined_functions() as $type => $definedFunctionNames) { + foreach ($definedFunctionNames as $definedFunctionName) { + if (false !== $namespaceSeparatorIndex = strrpos($definedFunctionName, '\\')) { + $definedFunctionNameBasename = substr($definedFunctionName, $namespaceSeparatorIndex + 1); + } else { + $definedFunctionNameBasename = $definedFunctionName; + } + + if ($definedFunctionNameBasename === $functionName) { + $candidates[] = '\\'.$definedFunctionName; + } + } + } + + if ($candidates) { + sort($candidates); + $last = array_pop($candidates).'"?'; + if ($candidates) { + $candidates = 'e.g. "'.implode('", "', $candidates).'" or "'.$last; + } else { + $candidates = '"'.$last; + } + $message .= "\nDid you mean to call ".$candidates; + } + + return new UndefinedFunctionException($message, $exception); + } +} diff --git a/lib/silex/vendor/symfony/debug/FatalErrorHandler/UndefinedMethodFatalErrorHandler.php b/lib/silex/vendor/symfony/debug/FatalErrorHandler/UndefinedMethodFatalErrorHandler.php new file mode 100644 index 000000000..6fa62b6f2 --- /dev/null +++ b/lib/silex/vendor/symfony/debug/FatalErrorHandler/UndefinedMethodFatalErrorHandler.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\Debug\FatalErrorHandler; + +use Symfony\Component\Debug\Exception\FatalErrorException; +use Symfony\Component\Debug\Exception\UndefinedMethodException; + +/** + * ErrorHandler for undefined methods. + * + * @author Grégoire Pineau + */ +class UndefinedMethodFatalErrorHandler implements FatalErrorHandlerInterface +{ + /** + * {@inheritdoc} + */ + public function handleError(array $error, FatalErrorException $exception) + { + preg_match('/^Call to undefined method (.*)::(.*)\(\)$/', $error['message'], $matches); + if (!$matches) { + return; + } + + $className = $matches[1]; + $methodName = $matches[2]; + + $message = sprintf('Attempted to call an undefined method named "%s" of class "%s".', $methodName, $className); + + if (!class_exists($className) || null === $methods = get_class_methods($className)) { + // failed to get the class or its methods on which an unknown method was called (for example on an anonymous class) + return new UndefinedMethodException($message, $exception); + } + + $candidates = array(); + foreach ($methods as $definedMethodName) { + $lev = levenshtein($methodName, $definedMethodName); + if ($lev <= strlen($methodName) / 3 || false !== strpos($definedMethodName, $methodName)) { + $candidates[] = $definedMethodName; + } + } + + if ($candidates) { + sort($candidates); + $last = array_pop($candidates).'"?'; + if ($candidates) { + $candidates = 'e.g. "'.implode('", "', $candidates).'" or "'.$last; + } else { + $candidates = '"'.$last; + } + + $message .= "\nDid you mean to call ".$candidates; + } + + return new UndefinedMethodException($message, $exception); + } +} diff --git a/lib/silex/vendor/symfony/debug/LICENSE b/lib/silex/vendor/symfony/debug/LICENSE new file mode 100644 index 000000000..21d7fb9e2 --- /dev/null +++ b/lib/silex/vendor/symfony/debug/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2004-2018 Fabien Potencier + +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/lib/silex/vendor/symfony/debug/README.md b/lib/silex/vendor/symfony/debug/README.md new file mode 100644 index 000000000..a1d16175c --- /dev/null +++ b/lib/silex/vendor/symfony/debug/README.md @@ -0,0 +1,13 @@ +Debug Component +=============== + +The Debug component provides tools to ease debugging PHP code. + +Resources +--------- + + * [Documentation](https://symfony.com/doc/current/components/debug/index.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/lib/silex/vendor/symfony/debug/Resources/ext/README.md b/lib/silex/vendor/symfony/debug/Resources/ext/README.md new file mode 100644 index 000000000..25dccf076 --- /dev/null +++ b/lib/silex/vendor/symfony/debug/Resources/ext/README.md @@ -0,0 +1,134 @@ +Symfony Debug Extension for PHP 5 +================================= + +This extension publishes several functions to help building powerful debugging tools. +It is compatible with PHP 5.3, 5.4, 5.5 and 5.6; with ZTS and non-ZTS modes. +It is not required thus not provided for PHP 7. + +symfony_zval_info() +------------------- + +- exposes zval_hash/refcounts, allowing e.g. efficient exploration of arbitrary structures in PHP, +- does work with references, preventing memory copying. + +Its behavior is about the same as: + +```php + gettype($array[$key]), + 'zval_hash' => /* hashed memory address of $array[$key] */, + 'zval_refcount' => /* internal zval refcount of $array[$key] */, + 'zval_isref' => /* is_ref status of $array[$key] */, + ); + + switch ($info['type']) { + case 'object': + $info += array( + 'object_class' => get_class($array[$key]), + 'object_refcount' => /* internal object refcount of $array[$key] */, + 'object_hash' => spl_object_hash($array[$key]), + 'object_handle' => /* internal object handle $array[$key] */, + ); + break; + + case 'resource': + $info += array( + 'resource_handle' => (int) $array[$key], + 'resource_type' => get_resource_type($array[$key]), + 'resource_refcount' => /* internal resource refcount of $array[$key] */, + ); + break; + + case 'array': + $info += array( + 'array_count' => count($array[$key]), + ); + break; + + case 'string': + $info += array( + 'strlen' => strlen($array[$key]), + ); + break; + } + + return $info; +} +``` + +symfony_debug_backtrace() +------------------------- + +This function works like debug_backtrace(), except that it can fetch the full backtrace in case of fatal errors: + +```php +function foo() { fatal(); } +function bar() { foo(); } + +function sd() { var_dump(symfony_debug_backtrace()); } + +register_shutdown_function('sd'); + +bar(); + +/* Will output +Fatal error: Call to undefined function fatal() in foo.php on line 42 +array(3) { + [0]=> + array(2) { + ["function"]=> + string(2) "sd" + ["args"]=> + array(0) { + } + } + [1]=> + array(4) { + ["file"]=> + string(7) "foo.php" + ["line"]=> + int(1) + ["function"]=> + string(3) "foo" + ["args"]=> + array(0) { + } + } + [2]=> + array(4) { + ["file"]=> + string(102) "foo.php" + ["line"]=> + int(2) + ["function"]=> + string(3) "bar" + ["args"]=> + array(0) { + } + } +} +*/ +``` + +Usage +----- + +To enable the extension from source, run: + +``` + phpize + ./configure + make + sudo make install +``` diff --git a/lib/silex/vendor/symfony/debug/Resources/ext/config.m4 b/lib/silex/vendor/symfony/debug/Resources/ext/config.m4 new file mode 100644 index 000000000..3c5604715 --- /dev/null +++ b/lib/silex/vendor/symfony/debug/Resources/ext/config.m4 @@ -0,0 +1,63 @@ +dnl $Id$ +dnl config.m4 for extension symfony_debug + +dnl Comments in this file start with the string 'dnl'. +dnl Remove where necessary. This file will not work +dnl without editing. + +dnl If your extension references something external, use with: + +dnl PHP_ARG_WITH(symfony_debug, for symfony_debug support, +dnl Make sure that the comment is aligned: +dnl [ --with-symfony_debug Include symfony_debug support]) + +dnl Otherwise use enable: + +PHP_ARG_ENABLE(symfony_debug, whether to enable symfony_debug support, +dnl Make sure that the comment is aligned: +[ --enable-symfony_debug Enable symfony_debug support]) + +if test "$PHP_SYMFONY_DEBUG" != "no"; then + dnl Write more examples of tests here... + + dnl # --with-symfony_debug -> check with-path + dnl SEARCH_PATH="/usr/local /usr" # you might want to change this + dnl SEARCH_FOR="/include/symfony_debug.h" # you most likely want to change this + dnl if test -r $PHP_SYMFONY_DEBUG/$SEARCH_FOR; then # path given as parameter + dnl SYMFONY_DEBUG_DIR=$PHP_SYMFONY_DEBUG + dnl else # search default path list + dnl AC_MSG_CHECKING([for symfony_debug files in default path]) + dnl for i in $SEARCH_PATH ; do + dnl if test -r $i/$SEARCH_FOR; then + dnl SYMFONY_DEBUG_DIR=$i + dnl AC_MSG_RESULT(found in $i) + dnl fi + dnl done + dnl fi + dnl + dnl if test -z "$SYMFONY_DEBUG_DIR"; then + dnl AC_MSG_RESULT([not found]) + dnl AC_MSG_ERROR([Please reinstall the symfony_debug distribution]) + dnl fi + + dnl # --with-symfony_debug -> add include path + dnl PHP_ADD_INCLUDE($SYMFONY_DEBUG_DIR/include) + + dnl # --with-symfony_debug -> check for lib and symbol presence + dnl LIBNAME=symfony_debug # you may want to change this + dnl LIBSYMBOL=symfony_debug # you most likely want to change this + + dnl PHP_CHECK_LIBRARY($LIBNAME,$LIBSYMBOL, + dnl [ + dnl PHP_ADD_LIBRARY_WITH_PATH($LIBNAME, $SYMFONY_DEBUG_DIR/lib, SYMFONY_DEBUG_SHARED_LIBADD) + dnl AC_DEFINE(HAVE_SYMFONY_DEBUGLIB,1,[ ]) + dnl ],[ + dnl AC_MSG_ERROR([wrong symfony_debug lib version or lib not found]) + dnl ],[ + dnl -L$SYMFONY_DEBUG_DIR/lib -lm + dnl ]) + dnl + dnl PHP_SUBST(SYMFONY_DEBUG_SHARED_LIBADD) + + PHP_NEW_EXTENSION(symfony_debug, symfony_debug.c, $ext_shared) +fi diff --git a/lib/silex/vendor/symfony/debug/Resources/ext/config.w32 b/lib/silex/vendor/symfony/debug/Resources/ext/config.w32 new file mode 100644 index 000000000..487e69138 --- /dev/null +++ b/lib/silex/vendor/symfony/debug/Resources/ext/config.w32 @@ -0,0 +1,13 @@ +// $Id$ +// vim:ft=javascript + +// If your extension references something external, use ARG_WITH +// ARG_WITH("symfony_debug", "for symfony_debug support", "no"); + +// Otherwise, use ARG_ENABLE +// ARG_ENABLE("symfony_debug", "enable symfony_debug support", "no"); + +if (PHP_SYMFONY_DEBUG != "no") { + EXTENSION("symfony_debug", "symfony_debug.c"); +} + diff --git a/lib/silex/vendor/symfony/debug/Resources/ext/php_symfony_debug.h b/lib/silex/vendor/symfony/debug/Resources/ext/php_symfony_debug.h new file mode 100644 index 000000000..26d0e8c01 --- /dev/null +++ b/lib/silex/vendor/symfony/debug/Resources/ext/php_symfony_debug.h @@ -0,0 +1,60 @@ +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +#ifndef PHP_SYMFONY_DEBUG_H +#define PHP_SYMFONY_DEBUG_H + +extern zend_module_entry symfony_debug_module_entry; +#define phpext_symfony_debug_ptr &symfony_debug_module_entry + +#define PHP_SYMFONY_DEBUG_VERSION "2.7" + +#ifdef PHP_WIN32 +# define PHP_SYMFONY_DEBUG_API __declspec(dllexport) +#elif defined(__GNUC__) && __GNUC__ >= 4 +# define PHP_SYMFONY_DEBUG_API __attribute__ ((visibility("default"))) +#else +# define PHP_SYMFONY_DEBUG_API +#endif + +#ifdef ZTS +#include "TSRM.h" +#endif + +ZEND_BEGIN_MODULE_GLOBALS(symfony_debug) + intptr_t req_rand_init; + void (*old_error_cb)(int type, const char *error_filename, const uint error_lineno, const char *format, va_list args); + zval *debug_bt; +ZEND_END_MODULE_GLOBALS(symfony_debug) + +PHP_MINIT_FUNCTION(symfony_debug); +PHP_MSHUTDOWN_FUNCTION(symfony_debug); +PHP_RINIT_FUNCTION(symfony_debug); +PHP_RSHUTDOWN_FUNCTION(symfony_debug); +PHP_MINFO_FUNCTION(symfony_debug); +PHP_GINIT_FUNCTION(symfony_debug); +PHP_GSHUTDOWN_FUNCTION(symfony_debug); + +PHP_FUNCTION(symfony_zval_info); +PHP_FUNCTION(symfony_debug_backtrace); + +static char *_symfony_debug_memory_address_hash(void * TSRMLS_DC); +static const char *_symfony_debug_zval_type(zval *); +static const char* _symfony_debug_get_resource_type(long TSRMLS_DC); +static int _symfony_debug_get_resource_refcount(long TSRMLS_DC); + +void symfony_debug_error_cb(int type, const char *error_filename, const uint error_lineno, const char *format, va_list args); + +#ifdef ZTS +#define SYMFONY_DEBUG_G(v) TSRMG(symfony_debug_globals_id, zend_symfony_debug_globals *, v) +#else +#define SYMFONY_DEBUG_G(v) (symfony_debug_globals.v) +#endif + +#endif /* PHP_SYMFONY_DEBUG_H */ diff --git a/lib/silex/vendor/symfony/debug/Resources/ext/symfony_debug.c b/lib/silex/vendor/symfony/debug/Resources/ext/symfony_debug.c new file mode 100644 index 000000000..0d7cb6023 --- /dev/null +++ b/lib/silex/vendor/symfony/debug/Resources/ext/symfony_debug.c @@ -0,0 +1,283 @@ +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "php.h" +#ifdef ZTS +#include "TSRM.h" +#endif +#include "php_ini.h" +#include "ext/standard/info.h" +#include "php_symfony_debug.h" +#include "ext/standard/php_rand.h" +#include "ext/standard/php_lcg.h" +#include "ext/spl/php_spl.h" +#include "Zend/zend_gc.h" +#include "Zend/zend_builtin_functions.h" +#include "Zend/zend_extensions.h" /* for ZEND_EXTENSION_API_NO */ +#include "ext/standard/php_array.h" +#include "Zend/zend_interfaces.h" +#include "SAPI.h" + +#define IS_PHP_53 ZEND_EXTENSION_API_NO == 220090626 + +ZEND_DECLARE_MODULE_GLOBALS(symfony_debug) + +ZEND_BEGIN_ARG_INFO_EX(symfony_zval_arginfo, 0, 0, 2) + ZEND_ARG_INFO(0, key) + ZEND_ARG_ARRAY_INFO(0, array, 0) + ZEND_ARG_INFO(0, options) +ZEND_END_ARG_INFO() + +const zend_function_entry symfony_debug_functions[] = { + PHP_FE(symfony_zval_info, symfony_zval_arginfo) + PHP_FE(symfony_debug_backtrace, NULL) + PHP_FE_END +}; + +PHP_FUNCTION(symfony_debug_backtrace) +{ + if (zend_parse_parameters_none() == FAILURE) { + return; + } +#if IS_PHP_53 + zend_fetch_debug_backtrace(return_value, 1, 0 TSRMLS_CC); +#else + zend_fetch_debug_backtrace(return_value, 1, 0, 0 TSRMLS_CC); +#endif + + if (!SYMFONY_DEBUG_G(debug_bt)) { + return; + } + + php_array_merge(Z_ARRVAL_P(return_value), Z_ARRVAL_P(SYMFONY_DEBUG_G(debug_bt)), 0 TSRMLS_CC); +} + +PHP_FUNCTION(symfony_zval_info) +{ + zval *key = NULL, *arg = NULL; + zval **data = NULL; + HashTable *array = NULL; + long options = 0; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zh|l", &key, &array, &options) == FAILURE) { + return; + } + + switch (Z_TYPE_P(key)) { + case IS_STRING: + if (zend_symtable_find(array, Z_STRVAL_P(key), Z_STRLEN_P(key) + 1, (void **)&data) == FAILURE) { + return; + } + break; + case IS_LONG: + if (zend_hash_index_find(array, Z_LVAL_P(key), (void **)&data)) { + return; + } + break; + } + + arg = *data; + + array_init(return_value); + + add_assoc_string(return_value, "type", (char *)_symfony_debug_zval_type(arg), 1); + add_assoc_stringl(return_value, "zval_hash", _symfony_debug_memory_address_hash((void *)arg TSRMLS_CC), 16, 0); + add_assoc_long(return_value, "zval_refcount", Z_REFCOUNT_P(arg)); + add_assoc_bool(return_value, "zval_isref", (zend_bool)Z_ISREF_P(arg)); + + if (Z_TYPE_P(arg) == IS_OBJECT) { + char hash[33] = {0}; + + php_spl_object_hash(arg, (char *)hash TSRMLS_CC); + add_assoc_stringl(return_value, "object_class", (char *)Z_OBJCE_P(arg)->name, Z_OBJCE_P(arg)->name_length, 1); + add_assoc_long(return_value, "object_refcount", EG(objects_store).object_buckets[Z_OBJ_HANDLE_P(arg)].bucket.obj.refcount); + add_assoc_string(return_value, "object_hash", hash, 1); + add_assoc_long(return_value, "object_handle", Z_OBJ_HANDLE_P(arg)); + } else if (Z_TYPE_P(arg) == IS_ARRAY) { + add_assoc_long(return_value, "array_count", zend_hash_num_elements(Z_ARRVAL_P(arg))); + } else if(Z_TYPE_P(arg) == IS_RESOURCE) { + add_assoc_long(return_value, "resource_handle", Z_LVAL_P(arg)); + add_assoc_string(return_value, "resource_type", (char *)_symfony_debug_get_resource_type(Z_LVAL_P(arg) TSRMLS_CC), 1); + add_assoc_long(return_value, "resource_refcount", _symfony_debug_get_resource_refcount(Z_LVAL_P(arg) TSRMLS_CC)); + } else if (Z_TYPE_P(arg) == IS_STRING) { + add_assoc_long(return_value, "strlen", Z_STRLEN_P(arg)); + } +} + +void symfony_debug_error_cb(int type, const char *error_filename, const uint error_lineno, const char *format, va_list args) +{ + TSRMLS_FETCH(); + zval *retval; + + switch (type) { + case E_ERROR: + case E_PARSE: + case E_CORE_ERROR: + case E_CORE_WARNING: + case E_COMPILE_ERROR: + case E_COMPILE_WARNING: + ALLOC_INIT_ZVAL(retval); +#if IS_PHP_53 + zend_fetch_debug_backtrace(retval, 1, 0 TSRMLS_CC); +#else + zend_fetch_debug_backtrace(retval, 1, 0, 0 TSRMLS_CC); +#endif + SYMFONY_DEBUG_G(debug_bt) = retval; + } + + SYMFONY_DEBUG_G(old_error_cb)(type, error_filename, error_lineno, format, args); +} + +static const char* _symfony_debug_get_resource_type(long rsid TSRMLS_DC) +{ + const char *res_type; + res_type = zend_rsrc_list_get_rsrc_type(rsid TSRMLS_CC); + + if (!res_type) { + return "Unknown"; + } + + return res_type; +} + +static int _symfony_debug_get_resource_refcount(long rsid TSRMLS_DC) +{ + zend_rsrc_list_entry *le; + + if (zend_hash_index_find(&EG(regular_list), rsid, (void **) &le)==SUCCESS) { + return le->refcount; + } + + return 0; +} + +static char *_symfony_debug_memory_address_hash(void *address TSRMLS_DC) +{ + char *result = NULL; + intptr_t address_rand; + + if (!SYMFONY_DEBUG_G(req_rand_init)) { + if (!BG(mt_rand_is_seeded)) { + php_mt_srand(GENERATE_SEED() TSRMLS_CC); + } + SYMFONY_DEBUG_G(req_rand_init) = (intptr_t)php_mt_rand(TSRMLS_C); + } + + address_rand = (intptr_t)address ^ SYMFONY_DEBUG_G(req_rand_init); + + spprintf(&result, 17, "%016zx", address_rand); + + return result; +} + +static const char *_symfony_debug_zval_type(zval *zv) +{ + switch (Z_TYPE_P(zv)) { + case IS_NULL: + return "NULL"; + break; + + case IS_BOOL: + return "boolean"; + break; + + case IS_LONG: + return "integer"; + break; + + case IS_DOUBLE: + return "double"; + break; + + case IS_STRING: + return "string"; + break; + + case IS_ARRAY: + return "array"; + break; + + case IS_OBJECT: + return "object"; + + case IS_RESOURCE: + return "resource"; + + default: + return "unknown type"; + } +} + +zend_module_entry symfony_debug_module_entry = { + STANDARD_MODULE_HEADER, + "symfony_debug", + symfony_debug_functions, + PHP_MINIT(symfony_debug), + PHP_MSHUTDOWN(symfony_debug), + PHP_RINIT(symfony_debug), + PHP_RSHUTDOWN(symfony_debug), + PHP_MINFO(symfony_debug), + PHP_SYMFONY_DEBUG_VERSION, + PHP_MODULE_GLOBALS(symfony_debug), + PHP_GINIT(symfony_debug), + PHP_GSHUTDOWN(symfony_debug), + NULL, + STANDARD_MODULE_PROPERTIES_EX +}; + +#ifdef COMPILE_DL_SYMFONY_DEBUG +ZEND_GET_MODULE(symfony_debug) +#endif + +PHP_GINIT_FUNCTION(symfony_debug) +{ + memset(symfony_debug_globals, 0 , sizeof(*symfony_debug_globals)); +} + +PHP_GSHUTDOWN_FUNCTION(symfony_debug) +{ + +} + +PHP_MINIT_FUNCTION(symfony_debug) +{ + SYMFONY_DEBUG_G(old_error_cb) = zend_error_cb; + zend_error_cb = symfony_debug_error_cb; + + return SUCCESS; +} + +PHP_MSHUTDOWN_FUNCTION(symfony_debug) +{ + zend_error_cb = SYMFONY_DEBUG_G(old_error_cb); + + return SUCCESS; +} + +PHP_RINIT_FUNCTION(symfony_debug) +{ + return SUCCESS; +} + +PHP_RSHUTDOWN_FUNCTION(symfony_debug) +{ + return SUCCESS; +} + +PHP_MINFO_FUNCTION(symfony_debug) +{ + php_info_print_table_start(); + php_info_print_table_header(2, "Symfony Debug support", "enabled"); + php_info_print_table_header(2, "Symfony Debug version", PHP_SYMFONY_DEBUG_VERSION); + php_info_print_table_end(); +} diff --git a/lib/silex/vendor/symfony/debug/Resources/ext/tests/001.phpt b/lib/silex/vendor/symfony/debug/Resources/ext/tests/001.phpt new file mode 100644 index 000000000..4a87cd318 --- /dev/null +++ b/lib/silex/vendor/symfony/debug/Resources/ext/tests/001.phpt @@ -0,0 +1,155 @@ +--TEST-- +Test symfony_zval_info API +--SKIPIF-- + +--FILE-- + $int, + 'float' => $float, + 'str' => $str, + 'object' => $object, + 'array' => $array, + 'resource' => $resource, + 'null' => $null, + 'bool' => $bool, + 'refcount' => &$refcount2, +); + +var_dump(symfony_zval_info('int', $var)); +var_dump(symfony_zval_info('float', $var)); +var_dump(symfony_zval_info('str', $var)); +var_dump(symfony_zval_info('object', $var)); +var_dump(symfony_zval_info('array', $var)); +var_dump(symfony_zval_info('resource', $var)); +var_dump(symfony_zval_info('null', $var)); +var_dump(symfony_zval_info('bool', $var)); + +var_dump(symfony_zval_info('refcount', $var)); +var_dump(symfony_zval_info('not-exist', $var)); +?> +--EXPECTF-- +array(4) { + ["type"]=> + string(7) "integer" + ["zval_hash"]=> + string(16) "%s" + ["zval_refcount"]=> + int(2) + ["zval_isref"]=> + bool(false) +} +array(4) { + ["type"]=> + string(6) "double" + ["zval_hash"]=> + string(16) "%s" + ["zval_refcount"]=> + int(2) + ["zval_isref"]=> + bool(false) +} +array(5) { + ["type"]=> + string(6) "string" + ["zval_hash"]=> + string(16) "%s" + ["zval_refcount"]=> + int(2) + ["zval_isref"]=> + bool(false) + ["strlen"]=> + int(6) +} +array(8) { + ["type"]=> + string(6) "object" + ["zval_hash"]=> + string(16) "%s" + ["zval_refcount"]=> + int(2) + ["zval_isref"]=> + bool(false) + ["object_class"]=> + string(8) "stdClass" + ["object_refcount"]=> + int(1) + ["object_hash"]=> + string(32) "%s" + ["object_handle"]=> + int(%d) +} +array(5) { + ["type"]=> + string(5) "array" + ["zval_hash"]=> + string(16) "%s" + ["zval_refcount"]=> + int(2) + ["zval_isref"]=> + bool(false) + ["array_count"]=> + int(2) +} +array(7) { + ["type"]=> + string(8) "resource" + ["zval_hash"]=> + string(16) "%s" + ["zval_refcount"]=> + int(2) + ["zval_isref"]=> + bool(false) + ["resource_handle"]=> + int(%d) + ["resource_type"]=> + string(6) "stream" + ["resource_refcount"]=> + int(1) +} +array(4) { + ["type"]=> + string(4) "NULL" + ["zval_hash"]=> + string(16) "%s" + ["zval_refcount"]=> + int(2) + ["zval_isref"]=> + bool(false) +} +array(4) { + ["type"]=> + string(7) "boolean" + ["zval_hash"]=> + string(16) "%s" + ["zval_refcount"]=> + int(2) + ["zval_isref"]=> + bool(false) +} +array(4) { + ["type"]=> + string(7) "integer" + ["zval_hash"]=> + string(16) "%s" + ["zval_refcount"]=> + int(3) + ["zval_isref"]=> + bool(true) +} +NULL diff --git a/lib/silex/vendor/symfony/debug/Resources/ext/tests/002.phpt b/lib/silex/vendor/symfony/debug/Resources/ext/tests/002.phpt new file mode 100644 index 000000000..afc7bb490 --- /dev/null +++ b/lib/silex/vendor/symfony/debug/Resources/ext/tests/002.phpt @@ -0,0 +1,65 @@ +--TEST-- +Test symfony_debug_backtrace in case of fatal error +--SKIPIF-- + +--FILE-- + +--EXPECTF-- +Fatal error: Call to undefined function notexist() in %s on line %d +Array +( + [0] => Array + ( + [function] => bt + [args] => Array + ( + ) + + ) + + [1] => Array + ( + [file] => %s + [line] => %d + [function] => foo + [args] => Array + ( + ) + + ) + + [2] => Array + ( + [file] => %s + [line] => %d + [function] => bar + [args] => Array + ( + ) + + ) + +) diff --git a/lib/silex/vendor/symfony/debug/Resources/ext/tests/002_1.phpt b/lib/silex/vendor/symfony/debug/Resources/ext/tests/002_1.phpt new file mode 100644 index 000000000..86de3e177 --- /dev/null +++ b/lib/silex/vendor/symfony/debug/Resources/ext/tests/002_1.phpt @@ -0,0 +1,48 @@ +--TEST-- +Test symfony_debug_backtrace in case of non fatal error +--SKIPIF-- + +--FILE-- + +--EXPECTF-- +Array +( + [0] => Array + ( + [file] => %s + [line] => %d + [function] => bt + [args] => Array + ( + ) + + ) + + [1] => Array + ( + [file] => %s + [line] => %d + [function] => bar + [args] => Array + ( + ) + + ) + +) diff --git a/lib/silex/vendor/symfony/debug/Resources/ext/tests/003.phpt b/lib/silex/vendor/symfony/debug/Resources/ext/tests/003.phpt new file mode 100644 index 000000000..ce3c4e0ab --- /dev/null +++ b/lib/silex/vendor/symfony/debug/Resources/ext/tests/003.phpt @@ -0,0 +1,87 @@ +--TEST-- +Test ErrorHandler in case of fatal error +--SKIPIF-- + +--FILE-- +setExceptionHandler('print_r'); + +if (function_exists('xdebug_disable')) { + xdebug_disable(); +} + +bar(); +?> +--EXPECTF-- +Fatal error: Call to undefined function Symfony\Component\Debug\notexist() in %s on line %d +Symfony\Component\Debug\Exception\UndefinedFunctionException Object +( + [message:protected] => Attempted to call function "notexist" from namespace "Symfony\Component\Debug". + [string:Exception:private] => + [code:protected] => 0 + [file:protected] => %s + [line:protected] => %d + [trace:Exception:private] => Array + ( + [0] => Array + ( +%A [function] => Symfony\Component\Debug\foo +%A [args] => Array + ( + ) + + ) + + [1] => Array + ( +%A [function] => Symfony\Component\Debug\bar +%A [args] => Array + ( + ) + + ) +%A + ) + + [previous:Exception:private] => + [severity:protected] => 1 +) diff --git a/lib/silex/vendor/symfony/debug/Tests/DebugClassLoaderTest.php b/lib/silex/vendor/symfony/debug/Tests/DebugClassLoaderTest.php new file mode 100644 index 000000000..0219f5335 --- /dev/null +++ b/lib/silex/vendor/symfony/debug/Tests/DebugClassLoaderTest.php @@ -0,0 +1,425 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Debug\Tests; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Debug\DebugClassLoader; +use Symfony\Component\Debug\ErrorHandler; + +class DebugClassLoaderTest extends TestCase +{ + /** + * @var int Error reporting level before running tests + */ + private $errorReporting; + + private $loader; + + protected function setUp() + { + $this->errorReporting = error_reporting(E_ALL); + $this->loader = new ClassLoader(); + spl_autoload_register(array($this->loader, 'loadClass'), true, true); + DebugClassLoader::enable(); + } + + protected function tearDown() + { + DebugClassLoader::disable(); + spl_autoload_unregister(array($this->loader, 'loadClass')); + error_reporting($this->errorReporting); + } + + public function testIdempotence() + { + DebugClassLoader::enable(); + + $functions = spl_autoload_functions(); + foreach ($functions as $function) { + if (is_array($function) && $function[0] instanceof DebugClassLoader) { + $reflClass = new \ReflectionClass($function[0]); + $reflProp = $reflClass->getProperty('classLoader'); + $reflProp->setAccessible(true); + + $this->assertNotInstanceOf('Symfony\Component\Debug\DebugClassLoader', $reflProp->getValue($function[0])); + + return; + } + } + + $this->fail('DebugClassLoader did not register'); + } + + /** + * @expectedException \Exception + * @expectedExceptionMessage boo + */ + public function testThrowingClass() + { + try { + class_exists(__NAMESPACE__.'\Fixtures\Throwing'); + $this->fail('Exception expected'); + } catch (\Exception $e) { + $this->assertSame('boo', $e->getMessage()); + } + + // the second call also should throw + class_exists(__NAMESPACE__.'\Fixtures\Throwing'); + } + + public function testUnsilencing() + { + if (\PHP_VERSION_ID >= 70000) { + $this->markTestSkipped('PHP7 throws exceptions, unsilencing is not required anymore.'); + } + if (defined('HHVM_VERSION')) { + $this->markTestSkipped('HHVM is not handled in this test case.'); + } + + ob_start(); + + $this->iniSet('log_errors', 0); + $this->iniSet('display_errors', 1); + + // See below: this will fail with parse error + // but this should not be @-silenced. + @class_exists(__NAMESPACE__.'\TestingUnsilencing', true); + + $output = ob_get_clean(); + + $this->assertStringMatchesFormat('%aParse error%a', $output); + } + + public function testStacking() + { + // the ContextErrorException must not be loaded to test the workaround + // for https://bugs.php.net/65322. + if (class_exists('Symfony\Component\Debug\Exception\ContextErrorException', false)) { + $this->markTestSkipped('The ContextErrorException class is already loaded.'); + } + if (defined('HHVM_VERSION')) { + $this->markTestSkipped('HHVM is not handled in this test case.'); + } + + ErrorHandler::register(); + + try { + // Trigger autoloading + E_STRICT at compile time + // which in turn triggers $errorHandler->handle() + // that again triggers autoloading for ContextErrorException. + // Error stacking works around the bug above and everything is fine. + + eval(' + namespace '.__NAMESPACE__.'; + class ChildTestingStacking extends TestingStacking { function foo($bar) {} } + '); + $this->fail('ContextErrorException expected'); + } catch (\ErrorException $exception) { + // if an exception is thrown, the test passed + $this->assertStringStartsWith(__FILE__, $exception->getFile()); + if (\PHP_VERSION_ID < 70000) { + $this->assertRegExp('/^Runtime Notice: Declaration/', $exception->getMessage()); + $this->assertEquals(E_STRICT, $exception->getSeverity()); + } else { + $this->assertRegExp('/^Warning: Declaration/', $exception->getMessage()); + $this->assertEquals(E_WARNING, $exception->getSeverity()); + } + } finally { + restore_error_handler(); + restore_exception_handler(); + } + } + + /** + * @expectedException \RuntimeException + * @expectedExceptionMessage Case mismatch between loaded and declared class names + */ + public function testNameCaseMismatch() + { + class_exists(__NAMESPACE__.'\TestingCaseMismatch', true); + } + + /** + * @expectedException \RuntimeException + * @expectedExceptionMessage Case mismatch between class and real file names + */ + public function testFileCaseMismatch() + { + if (!file_exists(__DIR__.'/Fixtures/CaseMismatch.php')) { + $this->markTestSkipped('Can only be run on case insensitive filesystems'); + } + + class_exists(__NAMESPACE__.'\Fixtures\CaseMismatch', true); + } + + /** + * @expectedException \RuntimeException + * @expectedExceptionMessage Case mismatch between loaded and declared class names + */ + public function testPsr4CaseMismatch() + { + class_exists(__NAMESPACE__.'\Fixtures\Psr4CaseMismatch', true); + } + + public function testNotPsr0() + { + $this->assertTrue(class_exists(__NAMESPACE__.'\Fixtures\NotPSR0', true)); + } + + public function testNotPsr0Bis() + { + $this->assertTrue(class_exists(__NAMESPACE__.'\Fixtures\NotPSR0bis', true)); + } + + public function testClassAlias() + { + $this->assertTrue(class_exists(__NAMESPACE__.'\Fixtures\ClassAlias', true)); + } + + /** + * @dataProvider provideDeprecatedSuper + */ + public function testDeprecatedSuper($class, $super, $type) + { + set_error_handler(function () { return false; }); + $e = error_reporting(0); + trigger_error('', E_USER_DEPRECATED); + + class_exists('Test\\'.__NAMESPACE__.'\\'.$class, true); + + error_reporting($e); + restore_error_handler(); + + $lastError = error_get_last(); + unset($lastError['file'], $lastError['line']); + + $xError = array( + 'type' => E_USER_DEPRECATED, + 'message' => 'The "Test\Symfony\Component\Debug\Tests\\'.$class.'" class '.$type.' "Symfony\Component\Debug\Tests\Fixtures\\'.$super.'" that is deprecated but this is a test deprecation notice.', + ); + + $this->assertSame($xError, $lastError); + } + + public function provideDeprecatedSuper() + { + return array( + array('DeprecatedInterfaceClass', 'DeprecatedInterface', 'implements'), + array('DeprecatedParentClass', 'DeprecatedClass', 'extends'), + ); + } + + public function testInterfaceExtendsDeprecatedInterface() + { + set_error_handler(function () { return false; }); + $e = error_reporting(0); + trigger_error('', E_USER_NOTICE); + + class_exists('Test\\'.__NAMESPACE__.'\\NonDeprecatedInterfaceClass', true); + + error_reporting($e); + restore_error_handler(); + + $lastError = error_get_last(); + unset($lastError['file'], $lastError['line']); + + $xError = array( + 'type' => E_USER_NOTICE, + 'message' => '', + ); + + $this->assertSame($xError, $lastError); + } + + public function testDeprecatedSuperInSameNamespace() + { + set_error_handler(function () { return false; }); + $e = error_reporting(0); + trigger_error('', E_USER_NOTICE); + + class_exists('Symfony\Bridge\Debug\Tests\Fixtures\ExtendsDeprecatedParent', true); + + error_reporting($e); + restore_error_handler(); + + $lastError = error_get_last(); + unset($lastError['file'], $lastError['line']); + + $xError = array( + 'type' => E_USER_NOTICE, + 'message' => '', + ); + + $this->assertSame($xError, $lastError); + } + + public function testReservedForPhp7() + { + if (\PHP_VERSION_ID >= 70000) { + $this->markTestSkipped('PHP7 already prevents using reserved names.'); + } + + set_error_handler(function () { return false; }); + $e = error_reporting(0); + trigger_error('', E_USER_NOTICE); + + class_exists('Test\\'.__NAMESPACE__.'\\Float', true); + + error_reporting($e); + restore_error_handler(); + + $lastError = error_get_last(); + unset($lastError['file'], $lastError['line']); + + $xError = array( + 'type' => E_USER_DEPRECATED, + 'message' => 'The "Test\Symfony\Component\Debug\Tests\Float" class uses the reserved name "Float", it will break on PHP 7 and higher', + ); + + $this->assertSame($xError, $lastError); + } + + public function testExtendedFinalClass() + { + set_error_handler(function () { return false; }); + $e = error_reporting(0); + trigger_error('', E_USER_NOTICE); + + class_exists('Test\\'.__NAMESPACE__.'\\ExtendsFinalClass', true); + + error_reporting($e); + restore_error_handler(); + + $lastError = error_get_last(); + unset($lastError['file'], $lastError['line']); + + $xError = array( + 'type' => E_USER_DEPRECATED, + 'message' => 'The "Symfony\Component\Debug\Tests\Fixtures\FinalClass" class is considered final since version 3.3. It may change without further notice as of its next major version. You should not extend it from "Test\Symfony\Component\Debug\Tests\ExtendsFinalClass".', + ); + + $this->assertSame($xError, $lastError); + } + + public function testExtendedFinalMethod() + { + set_error_handler(function () { return false; }); + $e = error_reporting(0); + trigger_error('', E_USER_NOTICE); + + class_exists(__NAMESPACE__.'\\Fixtures\\ExtendedFinalMethod', true); + + error_reporting($e); + restore_error_handler(); + + $lastError = error_get_last(); + unset($lastError['file'], $lastError['line']); + + $xError = array( + 'type' => E_USER_DEPRECATED, + 'message' => 'The "Symfony\Component\Debug\Tests\Fixtures\FinalMethod::finalMethod()" method is considered final since version 3.3. It may change without further notice as of its next major version. You should not extend it from "Symfony\Component\Debug\Tests\Fixtures\ExtendedFinalMethod".', + ); + + $this->assertSame($xError, $lastError); + } + + public function testExtendedDeprecatedMethodDoesntTriggerAnyNotice() + { + set_error_handler(function () { return false; }); + $e = error_reporting(0); + trigger_error('', E_USER_NOTICE); + + class_exists('Test\\'.__NAMESPACE__.'\\ExtendsAnnotatedClass', true); + + error_reporting($e); + restore_error_handler(); + + $lastError = error_get_last(); + unset($lastError['file'], $lastError['line']); + + $this->assertSame(array('type' => E_USER_NOTICE, 'message' => ''), $lastError); + } + + public function testInternalsUse() + { + $deprecations = array(); + set_error_handler(function ($type, $msg) use (&$deprecations) { $deprecations[] = $msg; }); + $e = error_reporting(E_USER_DEPRECATED); + + class_exists('Test\\'.__NAMESPACE__.'\\ExtendsInternals', true); + + error_reporting($e); + restore_error_handler(); + + $this->assertSame($deprecations, array( + 'The "Symfony\Component\Debug\Tests\Fixtures\InternalClass" class is considered internal since version 3.4. It may change without further notice. You should not use it from "Test\Symfony\Component\Debug\Tests\ExtendsInternalsParent".', + 'The "Symfony\Component\Debug\Tests\Fixtures\InternalInterface" interface is considered internal. It may change without further notice. You should not use it from "Test\Symfony\Component\Debug\Tests\ExtendsInternalsParent".', + 'The "Symfony\Component\Debug\Tests\Fixtures\InternalTrait" trait is considered internal. It may change without further notice. You should not use it from "Test\Symfony\Component\Debug\Tests\ExtendsInternals".', + 'The "Symfony\Component\Debug\Tests\Fixtures\InternalTrait2::internalMethod()" method is considered internal since version 3.4. It may change without further notice. You should not extend it from "Test\Symfony\Component\Debug\Tests\ExtendsInternals".', + )); + } +} + +class ClassLoader +{ + public function loadClass($class) + { + } + + public function getClassMap() + { + return array(__NAMESPACE__.'\Fixtures\NotPSR0bis' => __DIR__.'/Fixtures/notPsr0Bis.php'); + } + + public function findFile($class) + { + $fixtureDir = __DIR__.DIRECTORY_SEPARATOR.'Fixtures'.DIRECTORY_SEPARATOR; + + if (__NAMESPACE__.'\TestingUnsilencing' === $class) { + eval('-- parse error --'); + } elseif (__NAMESPACE__.'\TestingStacking' === $class) { + eval('namespace '.__NAMESPACE__.'; class TestingStacking { function foo() {} }'); + } elseif (__NAMESPACE__.'\TestingCaseMismatch' === $class) { + eval('namespace '.__NAMESPACE__.'; class TestingCaseMisMatch {}'); + } elseif (__NAMESPACE__.'\Fixtures\Psr4CaseMismatch' === $class) { + return $fixtureDir.'psr4'.DIRECTORY_SEPARATOR.'Psr4CaseMismatch.php'; + } elseif (__NAMESPACE__.'\Fixtures\NotPSR0' === $class) { + return $fixtureDir.'reallyNotPsr0.php'; + } elseif (__NAMESPACE__.'\Fixtures\NotPSR0bis' === $class) { + return $fixtureDir.'notPsr0Bis.php'; + } elseif ('Symfony\Bridge\Debug\Tests\Fixtures\ExtendsDeprecatedParent' === $class) { + eval('namespace Symfony\Bridge\Debug\Tests\Fixtures; class ExtendsDeprecatedParent extends \\'.__NAMESPACE__.'\Fixtures\DeprecatedClass {}'); + } elseif ('Test\\'.__NAMESPACE__.'\DeprecatedParentClass' === $class) { + eval('namespace Test\\'.__NAMESPACE__.'; class DeprecatedParentClass extends \\'.__NAMESPACE__.'\Fixtures\DeprecatedClass {}'); + } elseif ('Test\\'.__NAMESPACE__.'\DeprecatedInterfaceClass' === $class) { + eval('namespace Test\\'.__NAMESPACE__.'; class DeprecatedInterfaceClass implements \\'.__NAMESPACE__.'\Fixtures\DeprecatedInterface {}'); + } elseif ('Test\\'.__NAMESPACE__.'\NonDeprecatedInterfaceClass' === $class) { + eval('namespace Test\\'.__NAMESPACE__.'; class NonDeprecatedInterfaceClass implements \\'.__NAMESPACE__.'\Fixtures\NonDeprecatedInterface {}'); + } elseif ('Test\\'.__NAMESPACE__.'\Float' === $class) { + eval('namespace Test\\'.__NAMESPACE__.'; class Float {}'); + } elseif ('Test\\'.__NAMESPACE__.'\ExtendsFinalClass' === $class) { + eval('namespace Test\\'.__NAMESPACE__.'; class ExtendsFinalClass extends \\'.__NAMESPACE__.'\Fixtures\FinalClass {}'); + } elseif ('Test\\'.__NAMESPACE__.'\ExtendsAnnotatedClass' === $class) { + eval('namespace Test\\'.__NAMESPACE__.'; class ExtendsAnnotatedClass extends \\'.__NAMESPACE__.'\Fixtures\AnnotatedClass { + public function deprecatedMethod() { } + }'); + } elseif ('Test\\'.__NAMESPACE__.'\ExtendsInternals' === $class) { + eval('namespace Test\\'.__NAMESPACE__.'; class ExtendsInternals extends ExtendsInternalsParent { + use \\'.__NAMESPACE__.'\Fixtures\InternalTrait; + + public function internalMethod() { } + }'); + } elseif ('Test\\'.__NAMESPACE__.'\ExtendsInternalsParent' === $class) { + eval('namespace Test\\'.__NAMESPACE__.'; class ExtendsInternalsParent extends \\'.__NAMESPACE__.'\Fixtures\InternalClass implements \\'.__NAMESPACE__.'\Fixtures\InternalInterface { }'); + } + } +} diff --git a/lib/silex/vendor/symfony/debug/Tests/ErrorHandlerTest.php b/lib/silex/vendor/symfony/debug/Tests/ErrorHandlerTest.php new file mode 100644 index 000000000..a57e83ee4 --- /dev/null +++ b/lib/silex/vendor/symfony/debug/Tests/ErrorHandlerTest.php @@ -0,0 +1,548 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Debug\Tests; + +use PHPUnit\Framework\TestCase; +use Psr\Log\LogLevel; +use Symfony\Component\Debug\BufferingLogger; +use Symfony\Component\Debug\ErrorHandler; +use Symfony\Component\Debug\Exception\SilencedErrorContext; + +/** + * ErrorHandlerTest. + * + * @author Robert Schönthal + * @author Nicolas Grekas + */ +class ErrorHandlerTest extends TestCase +{ + public function testRegister() + { + $handler = ErrorHandler::register(); + + try { + $this->assertInstanceOf('Symfony\Component\Debug\ErrorHandler', $handler); + $this->assertSame($handler, ErrorHandler::register()); + + $newHandler = new ErrorHandler(); + + $this->assertSame($newHandler, ErrorHandler::register($newHandler, false)); + $h = set_error_handler('var_dump'); + restore_error_handler(); + $this->assertSame(array($handler, 'handleError'), $h); + + try { + $this->assertSame($newHandler, ErrorHandler::register($newHandler, true)); + $h = set_error_handler('var_dump'); + restore_error_handler(); + $this->assertSame(array($newHandler, 'handleError'), $h); + } catch (\Exception $e) { + } + + restore_error_handler(); + restore_exception_handler(); + + if (isset($e)) { + throw $e; + } + } catch (\Exception $e) { + } + + restore_error_handler(); + restore_exception_handler(); + + if (isset($e)) { + throw $e; + } + } + + public function testNotice() + { + ErrorHandler::register(); + + try { + self::triggerNotice($this); + $this->fail('ErrorException expected'); + } catch (\ErrorException $exception) { + // if an exception is thrown, the test passed + $this->assertEquals(E_NOTICE, $exception->getSeverity()); + $this->assertEquals(__FILE__, $exception->getFile()); + $this->assertRegExp('/^Notice: Undefined variable: (foo|bar)/', $exception->getMessage()); + + $trace = $exception->getTrace(); + + $this->assertEquals(__FILE__, $trace[0]['file']); + $this->assertEquals(__CLASS__, $trace[0]['class']); + $this->assertEquals('triggerNotice', $trace[0]['function']); + $this->assertEquals('::', $trace[0]['type']); + + $this->assertEquals(__FILE__, $trace[0]['file']); + $this->assertEquals(__CLASS__, $trace[1]['class']); + $this->assertEquals(__FUNCTION__, $trace[1]['function']); + $this->assertEquals('->', $trace[1]['type']); + } finally { + restore_error_handler(); + restore_exception_handler(); + } + } + + // dummy function to test trace in error handler. + private static function triggerNotice($that) + { + $that->assertSame('', $foo.$foo.$bar); + } + + public function testConstruct() + { + try { + $handler = ErrorHandler::register(); + $handler->throwAt(3, true); + $this->assertEquals(3 | E_RECOVERABLE_ERROR | E_USER_ERROR, $handler->throwAt(0)); + } finally { + restore_error_handler(); + restore_exception_handler(); + } + } + + public function testDefaultLogger() + { + try { + $handler = ErrorHandler::register(); + + $logger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock(); + + $handler->setDefaultLogger($logger, E_NOTICE); + $handler->setDefaultLogger($logger, array(E_USER_NOTICE => LogLevel::CRITICAL)); + + $loggers = array( + E_DEPRECATED => array(null, LogLevel::INFO), + E_USER_DEPRECATED => array(null, LogLevel::INFO), + E_NOTICE => array($logger, LogLevel::WARNING), + E_USER_NOTICE => array($logger, LogLevel::CRITICAL), + E_STRICT => array(null, LogLevel::WARNING), + E_WARNING => array(null, LogLevel::WARNING), + E_USER_WARNING => array(null, LogLevel::WARNING), + E_COMPILE_WARNING => array(null, LogLevel::WARNING), + E_CORE_WARNING => array(null, LogLevel::WARNING), + E_USER_ERROR => array(null, LogLevel::CRITICAL), + E_RECOVERABLE_ERROR => array(null, LogLevel::CRITICAL), + E_COMPILE_ERROR => array(null, LogLevel::CRITICAL), + E_PARSE => array(null, LogLevel::CRITICAL), + E_ERROR => array(null, LogLevel::CRITICAL), + E_CORE_ERROR => array(null, LogLevel::CRITICAL), + ); + $this->assertSame($loggers, $handler->setLoggers(array())); + } finally { + restore_error_handler(); + restore_exception_handler(); + } + } + + public function testHandleError() + { + try { + $handler = ErrorHandler::register(); + $handler->throwAt(0, true); + $this->assertFalse($handler->handleError(0, 'foo', 'foo.php', 12, array())); + + restore_error_handler(); + restore_exception_handler(); + + $handler = ErrorHandler::register(); + $handler->throwAt(3, true); + $this->assertFalse($handler->handleError(4, 'foo', 'foo.php', 12, array())); + + restore_error_handler(); + restore_exception_handler(); + + $handler = ErrorHandler::register(); + $handler->throwAt(3, true); + try { + $handler->handleError(4, 'foo', 'foo.php', 12, array()); + } catch (\ErrorException $e) { + $this->assertSame('Parse Error: foo', $e->getMessage()); + $this->assertSame(4, $e->getSeverity()); + $this->assertSame('foo.php', $e->getFile()); + $this->assertSame(12, $e->getLine()); + } + + restore_error_handler(); + restore_exception_handler(); + + $handler = ErrorHandler::register(); + $handler->throwAt(E_USER_DEPRECATED, true); + $this->assertFalse($handler->handleError(E_USER_DEPRECATED, 'foo', 'foo.php', 12, array())); + + restore_error_handler(); + restore_exception_handler(); + + $handler = ErrorHandler::register(); + $handler->throwAt(E_DEPRECATED, true); + $this->assertFalse($handler->handleError(E_DEPRECATED, 'foo', 'foo.php', 12, array())); + + restore_error_handler(); + restore_exception_handler(); + + $logger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock(); + + $warnArgCheck = function ($logLevel, $message, $context) { + $this->assertEquals('info', $logLevel); + $this->assertEquals('User Deprecated: foo', $message); + $this->assertArrayHasKey('exception', $context); + $exception = $context['exception']; + $this->assertInstanceOf(\ErrorException::class, $exception); + $this->assertSame('User Deprecated: foo', $exception->getMessage()); + $this->assertSame(E_USER_DEPRECATED, $exception->getSeverity()); + }; + + $logger + ->expects($this->once()) + ->method('log') + ->will($this->returnCallback($warnArgCheck)) + ; + + $handler = ErrorHandler::register(); + $handler->setDefaultLogger($logger, E_USER_DEPRECATED); + $this->assertTrue($handler->handleError(E_USER_DEPRECATED, 'foo', 'foo.php', 12, array())); + + restore_error_handler(); + restore_exception_handler(); + + $logger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock(); + + $line = null; + $logArgCheck = function ($level, $message, $context) use (&$line) { + $this->assertEquals('Notice: Undefined variable: undefVar', $message); + $this->assertArrayHasKey('exception', $context); + $exception = $context['exception']; + $this->assertInstanceOf(SilencedErrorContext::class, $exception); + $this->assertSame(E_NOTICE, $exception->getSeverity()); + $this->assertSame(__FILE__, $exception->getFile()); + $this->assertSame($line, $exception->getLine()); + $this->assertNotEmpty($exception->getTrace()); + $this->assertSame(1, $exception->count); + }; + + $logger + ->expects($this->once()) + ->method('log') + ->will($this->returnCallback($logArgCheck)) + ; + + $handler = ErrorHandler::register(); + $handler->setDefaultLogger($logger, E_NOTICE); + $handler->screamAt(E_NOTICE); + unset($undefVar); + $line = __LINE__ + 1; + @$undefVar++; + + restore_error_handler(); + restore_exception_handler(); + } catch (\Exception $e) { + restore_error_handler(); + restore_exception_handler(); + + throw $e; + } + } + + public function testHandleUserError() + { + try { + $handler = ErrorHandler::register(); + $handler->throwAt(0, true); + + $e = null; + $x = new \Exception('Foo'); + + try { + $f = new Fixtures\ToStringThrower($x); + $f .= ''; // Trigger $f->__toString() + } catch (\Exception $e) { + } + + $this->assertSame($x, $e); + } finally { + restore_error_handler(); + restore_exception_handler(); + } + } + + public function testHandleDeprecation() + { + $logArgCheck = function ($level, $message, $context) { + $this->assertEquals(LogLevel::INFO, $level); + $this->assertArrayHasKey('exception', $context); + $exception = $context['exception']; + $this->assertInstanceOf(\ErrorException::class, $exception); + $this->assertSame('User Deprecated: Foo deprecation', $exception->getMessage()); + }; + + $logger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock(); + $logger + ->expects($this->once()) + ->method('log') + ->will($this->returnCallback($logArgCheck)) + ; + + $handler = new ErrorHandler(); + $handler->setDefaultLogger($logger); + @$handler->handleError(E_USER_DEPRECATED, 'Foo deprecation', __FILE__, __LINE__, array()); + } + + /** + * @group no-hhvm + */ + public function testHandleException() + { + try { + $handler = ErrorHandler::register(); + + $exception = new \Exception('foo'); + + $logger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock(); + + $logArgCheck = function ($level, $message, $context) { + $this->assertSame('Uncaught Exception: foo', $message); + $this->assertArrayHasKey('exception', $context); + $this->assertInstanceOf(\Exception::class, $context['exception']); + }; + + $logger + ->expects($this->exactly(2)) + ->method('log') + ->will($this->returnCallback($logArgCheck)) + ; + + $handler->setDefaultLogger($logger, E_ERROR); + + try { + $handler->handleException($exception); + $this->fail('Exception expected'); + } catch (\Exception $e) { + $this->assertSame($exception, $e); + } + + $handler->setExceptionHandler(function ($e) use ($exception) { + $this->assertSame($exception, $e); + }); + + $handler->handleException($exception); + } finally { + restore_error_handler(); + restore_exception_handler(); + } + } + + /** + * @group legacy + */ + public function testErrorStacking() + { + try { + $handler = ErrorHandler::register(); + $handler->screamAt(E_USER_WARNING); + + $logger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock(); + + $logger + ->expects($this->exactly(2)) + ->method('log') + ->withConsecutive( + array($this->equalTo(LogLevel::WARNING), $this->equalTo('Dummy log')), + array($this->equalTo(LogLevel::DEBUG), $this->equalTo('User Warning: Silenced warning')) + ) + ; + + $handler->setDefaultLogger($logger, array(E_USER_WARNING => LogLevel::WARNING)); + + ErrorHandler::stackErrors(); + @trigger_error('Silenced warning', E_USER_WARNING); + $logger->log(LogLevel::WARNING, 'Dummy log'); + ErrorHandler::unstackErrors(); + } finally { + restore_error_handler(); + restore_exception_handler(); + } + } + + public function testBootstrappingLogger() + { + $bootLogger = new BufferingLogger(); + $handler = new ErrorHandler($bootLogger); + + $loggers = array( + E_DEPRECATED => array($bootLogger, LogLevel::INFO), + E_USER_DEPRECATED => array($bootLogger, LogLevel::INFO), + E_NOTICE => array($bootLogger, LogLevel::WARNING), + E_USER_NOTICE => array($bootLogger, LogLevel::WARNING), + E_STRICT => array($bootLogger, LogLevel::WARNING), + E_WARNING => array($bootLogger, LogLevel::WARNING), + E_USER_WARNING => array($bootLogger, LogLevel::WARNING), + E_COMPILE_WARNING => array($bootLogger, LogLevel::WARNING), + E_CORE_WARNING => array($bootLogger, LogLevel::WARNING), + E_USER_ERROR => array($bootLogger, LogLevel::CRITICAL), + E_RECOVERABLE_ERROR => array($bootLogger, LogLevel::CRITICAL), + E_COMPILE_ERROR => array($bootLogger, LogLevel::CRITICAL), + E_PARSE => array($bootLogger, LogLevel::CRITICAL), + E_ERROR => array($bootLogger, LogLevel::CRITICAL), + E_CORE_ERROR => array($bootLogger, LogLevel::CRITICAL), + ); + + $this->assertSame($loggers, $handler->setLoggers(array())); + + $handler->handleError(E_DEPRECATED, 'Foo message', __FILE__, 123, array()); + + $logs = $bootLogger->cleanLogs(); + + $this->assertCount(1, $logs); + $log = $logs[0]; + $this->assertSame('info', $log[0]); + $this->assertSame('Deprecated: Foo message', $log[1]); + $this->assertArrayHasKey('exception', $log[2]); + $exception = $log[2]['exception']; + $this->assertInstanceOf(\ErrorException::class, $exception); + $this->assertSame('Deprecated: Foo message', $exception->getMessage()); + $this->assertSame(__FILE__, $exception->getFile()); + $this->assertSame(123, $exception->getLine()); + $this->assertSame(E_DEPRECATED, $exception->getSeverity()); + + $bootLogger->log(LogLevel::WARNING, 'Foo message', array('exception' => $exception)); + + $mockLogger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock(); + $mockLogger->expects($this->once()) + ->method('log') + ->with(LogLevel::WARNING, 'Foo message', array('exception' => $exception)); + + $handler->setLoggers(array(E_DEPRECATED => array($mockLogger, LogLevel::WARNING))); + } + + /** + * @group no-hhvm + */ + public function testSettingLoggerWhenExceptionIsBuffered() + { + $bootLogger = new BufferingLogger(); + $handler = new ErrorHandler($bootLogger); + + $exception = new \Exception('Foo message'); + + $mockLogger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock(); + $mockLogger->expects($this->once()) + ->method('log') + ->with(LogLevel::CRITICAL, 'Uncaught Exception: Foo message', array('exception' => $exception)); + + $handler->setExceptionHandler(function () use ($handler, $mockLogger) { + $handler->setDefaultLogger($mockLogger); + }); + + $handler->handleException($exception); + } + + /** + * @group no-hhvm + */ + public function testHandleFatalError() + { + try { + $handler = ErrorHandler::register(); + + $error = array( + 'type' => E_PARSE, + 'message' => 'foo', + 'file' => 'bar', + 'line' => 123, + ); + + $logger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock(); + + $logArgCheck = function ($level, $message, $context) { + $this->assertEquals('Fatal Parse Error: foo', $message); + $this->assertArrayHasKey('exception', $context); + $this->assertInstanceOf(\Exception::class, $context['exception']); + }; + + $logger + ->expects($this->once()) + ->method('log') + ->will($this->returnCallback($logArgCheck)) + ; + + $handler->setDefaultLogger($logger, E_PARSE); + + $handler->handleFatalError($error); + + restore_error_handler(); + restore_exception_handler(); + } catch (\Exception $e) { + restore_error_handler(); + restore_exception_handler(); + + throw $e; + } + } + + /** + * @requires PHP 7 + */ + public function testHandleErrorException() + { + $exception = new \Error("Class 'Foo' not found"); + + $handler = new ErrorHandler(); + $handler->setExceptionHandler(function () use (&$args) { + $args = func_get_args(); + }); + + $handler->handleException($exception); + + $this->assertInstanceOf('Symfony\Component\Debug\Exception\ClassNotFoundException', $args[0]); + $this->assertStringStartsWith("Attempted to load class \"Foo\" from the global namespace.\nDid you forget a \"use\" statement", $args[0]->getMessage()); + } + + /** + * @group no-hhvm + */ + public function testHandleFatalErrorOnHHVM() + { + try { + $handler = ErrorHandler::register(); + + $logger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock(); + $logger + ->expects($this->once()) + ->method('log') + ->with( + $this->equalTo(LogLevel::CRITICAL), + $this->equalTo('Fatal Error: foo') + ) + ; + + $handler->setDefaultLogger($logger, E_ERROR); + + $error = array( + 'type' => E_ERROR + 0x1000000, // This error level is used by HHVM for fatal errors + 'message' => 'foo', + 'file' => 'bar', + 'line' => 123, + 'context' => array(123), + 'backtrace' => array(456), + ); + + call_user_func_array(array($handler, 'handleError'), $error); + $handler->handleFatalError($error); + } finally { + restore_error_handler(); + restore_exception_handler(); + } + } +} diff --git a/lib/silex/vendor/symfony/debug/Tests/Exception/FlattenExceptionTest.php b/lib/silex/vendor/symfony/debug/Tests/Exception/FlattenExceptionTest.php new file mode 100644 index 000000000..8fd1c7959 --- /dev/null +++ b/lib/silex/vendor/symfony/debug/Tests/Exception/FlattenExceptionTest.php @@ -0,0 +1,302 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Debug\Tests\Exception; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Debug\Exception\FlattenException; +use Symfony\Component\HttpFoundation\Exception\SuspiciousOperationException; +use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; +use Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException; +use Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException; +use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; +use Symfony\Component\HttpKernel\Exception\NotAcceptableHttpException; +use Symfony\Component\HttpKernel\Exception\ConflictHttpException; +use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; +use Symfony\Component\HttpKernel\Exception\GoneHttpException; +use Symfony\Component\HttpKernel\Exception\LengthRequiredHttpException; +use Symfony\Component\HttpKernel\Exception\PreconditionFailedHttpException; +use Symfony\Component\HttpKernel\Exception\PreconditionRequiredHttpException; +use Symfony\Component\HttpKernel\Exception\ServiceUnavailableHttpException; +use Symfony\Component\HttpKernel\Exception\TooManyRequestsHttpException; +use Symfony\Component\HttpKernel\Exception\UnsupportedMediaTypeHttpException; + +class FlattenExceptionTest extends TestCase +{ + public function testStatusCode() + { + $flattened = FlattenException::create(new \RuntimeException(), 403); + $this->assertEquals('403', $flattened->getStatusCode()); + + $flattened = FlattenException::create(new \RuntimeException()); + $this->assertEquals('500', $flattened->getStatusCode()); + + $flattened = FlattenException::create(new NotFoundHttpException()); + $this->assertEquals('404', $flattened->getStatusCode()); + + $flattened = FlattenException::create(new UnauthorizedHttpException('Basic realm="My Realm"')); + $this->assertEquals('401', $flattened->getStatusCode()); + + $flattened = FlattenException::create(new BadRequestHttpException()); + $this->assertEquals('400', $flattened->getStatusCode()); + + $flattened = FlattenException::create(new NotAcceptableHttpException()); + $this->assertEquals('406', $flattened->getStatusCode()); + + $flattened = FlattenException::create(new ConflictHttpException()); + $this->assertEquals('409', $flattened->getStatusCode()); + + $flattened = FlattenException::create(new MethodNotAllowedHttpException(array('POST'))); + $this->assertEquals('405', $flattened->getStatusCode()); + + $flattened = FlattenException::create(new AccessDeniedHttpException()); + $this->assertEquals('403', $flattened->getStatusCode()); + + $flattened = FlattenException::create(new GoneHttpException()); + $this->assertEquals('410', $flattened->getStatusCode()); + + $flattened = FlattenException::create(new LengthRequiredHttpException()); + $this->assertEquals('411', $flattened->getStatusCode()); + + $flattened = FlattenException::create(new PreconditionFailedHttpException()); + $this->assertEquals('412', $flattened->getStatusCode()); + + $flattened = FlattenException::create(new PreconditionRequiredHttpException()); + $this->assertEquals('428', $flattened->getStatusCode()); + + $flattened = FlattenException::create(new ServiceUnavailableHttpException()); + $this->assertEquals('503', $flattened->getStatusCode()); + + $flattened = FlattenException::create(new TooManyRequestsHttpException()); + $this->assertEquals('429', $flattened->getStatusCode()); + + $flattened = FlattenException::create(new UnsupportedMediaTypeHttpException()); + $this->assertEquals('415', $flattened->getStatusCode()); + + if (class_exists(SuspiciousOperationException::class)) { + $flattened = FlattenException::create(new SuspiciousOperationException()); + $this->assertEquals('400', $flattened->getStatusCode()); + } + } + + public function testHeadersForHttpException() + { + $flattened = FlattenException::create(new MethodNotAllowedHttpException(array('POST'))); + $this->assertEquals(array('Allow' => 'POST'), $flattened->getHeaders()); + + $flattened = FlattenException::create(new UnauthorizedHttpException('Basic realm="My Realm"')); + $this->assertEquals(array('WWW-Authenticate' => 'Basic realm="My Realm"'), $flattened->getHeaders()); + + $flattened = FlattenException::create(new ServiceUnavailableHttpException('Fri, 31 Dec 1999 23:59:59 GMT')); + $this->assertEquals(array('Retry-After' => 'Fri, 31 Dec 1999 23:59:59 GMT'), $flattened->getHeaders()); + + $flattened = FlattenException::create(new ServiceUnavailableHttpException(120)); + $this->assertEquals(array('Retry-After' => 120), $flattened->getHeaders()); + + $flattened = FlattenException::create(new TooManyRequestsHttpException('Fri, 31 Dec 1999 23:59:59 GMT')); + $this->assertEquals(array('Retry-After' => 'Fri, 31 Dec 1999 23:59:59 GMT'), $flattened->getHeaders()); + + $flattened = FlattenException::create(new TooManyRequestsHttpException(120)); + $this->assertEquals(array('Retry-After' => 120), $flattened->getHeaders()); + } + + /** + * @dataProvider flattenDataProvider + */ + public function testFlattenHttpException(\Exception $exception, $statusCode) + { + $flattened = FlattenException::create($exception); + $flattened2 = FlattenException::create($exception); + + $flattened->setPrevious($flattened2); + + $this->assertEquals($exception->getMessage(), $flattened->getMessage(), 'The message is copied from the original exception.'); + $this->assertEquals($exception->getCode(), $flattened->getCode(), 'The code is copied from the original exception.'); + $this->assertInstanceOf($flattened->getClass(), $exception, 'The class is set to the class of the original exception'); + } + + /** + * @dataProvider flattenDataProvider + */ + public function testPrevious(\Exception $exception, $statusCode) + { + $flattened = FlattenException::create($exception); + $flattened2 = FlattenException::create($exception); + + $flattened->setPrevious($flattened2); + + $this->assertSame($flattened2, $flattened->getPrevious()); + + $this->assertSame(array($flattened2), $flattened->getAllPrevious()); + } + + /** + * @requires PHP 7.0 + */ + public function testPreviousError() + { + $exception = new \Exception('test', 123, new \ParseError('Oh noes!', 42)); + + $flattened = FlattenException::create($exception)->getPrevious(); + + $this->assertEquals($flattened->getMessage(), 'Parse error: Oh noes!', 'The message is copied from the original exception.'); + $this->assertEquals($flattened->getCode(), 42, 'The code is copied from the original exception.'); + $this->assertEquals($flattened->getClass(), 'Symfony\Component\Debug\Exception\FatalThrowableError', 'The class is set to the class of the original exception'); + } + + /** + * @dataProvider flattenDataProvider + */ + public function testLine(\Exception $exception) + { + $flattened = FlattenException::create($exception); + $this->assertSame($exception->getLine(), $flattened->getLine()); + } + + /** + * @dataProvider flattenDataProvider + */ + public function testFile(\Exception $exception) + { + $flattened = FlattenException::create($exception); + $this->assertSame($exception->getFile(), $flattened->getFile()); + } + + /** + * @dataProvider flattenDataProvider + */ + public function testToArray(\Exception $exception, $statusCode) + { + $flattened = FlattenException::create($exception); + $flattened->setTrace(array(), 'foo.php', 123); + + $this->assertEquals(array( + array( + 'message' => 'test', + 'class' => 'Exception', + 'trace' => array(array( + 'namespace' => '', 'short_class' => '', 'class' => '', 'type' => '', 'function' => '', 'file' => 'foo.php', 'line' => 123, + 'args' => array(), + )), + ), + ), $flattened->toArray()); + } + + public function flattenDataProvider() + { + return array( + array(new \Exception('test', 123), 500), + ); + } + + public function testArguments() + { + $dh = opendir(__DIR__); + $fh = tmpfile(); + + $incomplete = unserialize('O:14:"BogusTestClass":0:{}'); + + $exception = $this->createException(array( + (object) array('foo' => 1), + new NotFoundHttpException(), + $incomplete, + $dh, + $fh, + function () {}, + array(1, 2), + array('foo' => 123), + null, + true, + false, + 0, + 0.0, + '0', + '', + INF, + NAN, + )); + + $flattened = FlattenException::create($exception); + $trace = $flattened->getTrace(); + $args = $trace[1]['args']; + $array = $args[0][1]; + + closedir($dh); + fclose($fh); + + $i = 0; + $this->assertSame(array('object', 'stdClass'), $array[$i++]); + $this->assertSame(array('object', 'Symfony\Component\HttpKernel\Exception\NotFoundHttpException'), $array[$i++]); + $this->assertSame(array('incomplete-object', 'BogusTestClass'), $array[$i++]); + $this->assertSame(array('resource', defined('HHVM_VERSION') ? 'Directory' : 'stream'), $array[$i++]); + $this->assertSame(array('resource', 'stream'), $array[$i++]); + + $args = $array[$i++]; + $this->assertSame($args[0], 'object'); + $this->assertTrue('Closure' === $args[1] || is_subclass_of($args[1], '\Closure'), 'Expect object class name to be Closure or a subclass of Closure.'); + + $this->assertSame(array('array', array(array('integer', 1), array('integer', 2))), $array[$i++]); + $this->assertSame(array('array', array('foo' => array('integer', 123))), $array[$i++]); + $this->assertSame(array('null', null), $array[$i++]); + $this->assertSame(array('boolean', true), $array[$i++]); + $this->assertSame(array('boolean', false), $array[$i++]); + $this->assertSame(array('integer', 0), $array[$i++]); + $this->assertSame(array('float', 0.0), $array[$i++]); + $this->assertSame(array('string', '0'), $array[$i++]); + $this->assertSame(array('string', ''), $array[$i++]); + $this->assertSame(array('float', INF), $array[$i++]); + + // assertEquals() does not like NAN values. + $this->assertEquals($array[$i][0], 'float'); + $this->assertTrue(is_nan($array[$i++][1])); + } + + public function testRecursionInArguments() + { + $a = null; + $a = array('foo', array(2, &$a)); + $exception = $this->createException($a); + + $flattened = FlattenException::create($exception); + $trace = $flattened->getTrace(); + $this->assertContains('*DEEP NESTED ARRAY*', serialize($trace)); + } + + public function testTooBigArray() + { + $a = array(); + for ($i = 0; $i < 20; ++$i) { + for ($j = 0; $j < 50; ++$j) { + for ($k = 0; $k < 10; ++$k) { + $a[$i][$j][$k] = 'value'; + } + } + } + $a[20] = 'value'; + $a[21] = 'value1'; + $exception = $this->createException($a); + + $flattened = FlattenException::create($exception); + $trace = $flattened->getTrace(); + + $this->assertSame($trace[1]['args'][0], array('array', array('array', '*SKIPPED over 10000 entries*'))); + + $serializeTrace = serialize($trace); + + $this->assertContains('*SKIPPED over 10000 entries*', $serializeTrace); + $this->assertNotContains('*value1*', $serializeTrace); + } + + private function createException($foo) + { + return new \Exception(); + } +} diff --git a/lib/silex/vendor/symfony/debug/Tests/ExceptionHandlerTest.php b/lib/silex/vendor/symfony/debug/Tests/ExceptionHandlerTest.php new file mode 100644 index 000000000..0285eff13 --- /dev/null +++ b/lib/silex/vendor/symfony/debug/Tests/ExceptionHandlerTest.php @@ -0,0 +1,133 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Debug\Tests; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Debug\ExceptionHandler; +use Symfony\Component\Debug\Exception\OutOfMemoryException; +use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; +use Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException; + +require_once __DIR__.'/HeaderMock.php'; + +class ExceptionHandlerTest extends TestCase +{ + protected function setUp() + { + testHeader(); + } + + protected function tearDown() + { + testHeader(); + } + + public function testDebug() + { + $handler = new ExceptionHandler(false); + + ob_start(); + $handler->sendPhpResponse(new \RuntimeException('Foo')); + $response = ob_get_clean(); + + $this->assertContains('Whoops, looks like something went wrong.', $response); + $this->assertNotContains('
', $response); + + $handler = new ExceptionHandler(true); + + ob_start(); + $handler->sendPhpResponse(new \RuntimeException('Foo')); + $response = ob_get_clean(); + + $this->assertContains('Whoops, looks like something went wrong.', $response); + $this->assertContains('
', $response); + } + + public function testStatusCode() + { + $handler = new ExceptionHandler(false, 'iso8859-1'); + + ob_start(); + $handler->sendPhpResponse(new NotFoundHttpException('Foo')); + $response = ob_get_clean(); + + $this->assertContains('Sorry, the page you are looking for could not be found.', $response); + + $expectedHeaders = array( + array('HTTP/1.0 404', true, null), + array('Content-Type: text/html; charset=iso8859-1', true, null), + ); + + $this->assertSame($expectedHeaders, testHeader()); + } + + public function testHeaders() + { + $handler = new ExceptionHandler(false, 'iso8859-1'); + + ob_start(); + $handler->sendPhpResponse(new MethodNotAllowedHttpException(array('POST'))); + $response = ob_get_clean(); + + $expectedHeaders = array( + array('HTTP/1.0 405', true, null), + array('Allow: POST', false, null), + array('Content-Type: text/html; charset=iso8859-1', true, null), + ); + + $this->assertSame($expectedHeaders, testHeader()); + } + + public function testNestedExceptions() + { + $handler = new ExceptionHandler(true); + ob_start(); + $handler->sendPhpResponse(new \RuntimeException('Foo', 0, new \RuntimeException('Bar'))); + $response = ob_get_clean(); + + $this->assertStringMatchesFormat('%A

Foo

%A

Bar

%A', $response); + } + + public function testHandle() + { + $exception = new \Exception('foo'); + + $handler = $this->getMockBuilder('Symfony\Component\Debug\ExceptionHandler')->setMethods(array('sendPhpResponse'))->getMock(); + $handler + ->expects($this->exactly(2)) + ->method('sendPhpResponse'); + + $handler->handle($exception); + + $handler->setHandler(function ($e) use ($exception) { + $this->assertSame($exception, $e); + }); + + $handler->handle($exception); + } + + public function testHandleOutOfMemoryException() + { + $exception = new OutOfMemoryException('foo', 0, E_ERROR, __FILE__, __LINE__); + + $handler = $this->getMockBuilder('Symfony\Component\Debug\ExceptionHandler')->setMethods(array('sendPhpResponse'))->getMock(); + $handler + ->expects($this->once()) + ->method('sendPhpResponse'); + + $handler->setHandler(function ($e) { + $this->fail('OutOfMemoryException should bypass the handler'); + }); + + $handler->handle($exception); + } +} diff --git a/lib/silex/vendor/symfony/debug/Tests/FatalErrorHandler/ClassNotFoundFatalErrorHandlerTest.php b/lib/silex/vendor/symfony/debug/Tests/FatalErrorHandler/ClassNotFoundFatalErrorHandlerTest.php new file mode 100644 index 000000000..65c80fc1c --- /dev/null +++ b/lib/silex/vendor/symfony/debug/Tests/FatalErrorHandler/ClassNotFoundFatalErrorHandlerTest.php @@ -0,0 +1,176 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Debug\Tests\FatalErrorHandler; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Debug\Exception\FatalErrorException; +use Symfony\Component\Debug\FatalErrorHandler\ClassNotFoundFatalErrorHandler; +use Symfony\Component\Debug\DebugClassLoader; +use Composer\Autoload\ClassLoader as ComposerClassLoader; + +class ClassNotFoundFatalErrorHandlerTest extends TestCase +{ + public static function setUpBeforeClass() + { + foreach (spl_autoload_functions() as $function) { + if (!is_array($function)) { + continue; + } + + // get class loaders wrapped by DebugClassLoader + if ($function[0] instanceof DebugClassLoader) { + $function = $function[0]->getClassLoader(); + } + + if ($function[0] instanceof ComposerClassLoader) { + $function[0]->add('Symfony_Component_Debug_Tests_Fixtures', dirname(dirname(dirname(dirname(dirname(__DIR__)))))); + break; + } + } + } + + /** + * @dataProvider provideClassNotFoundData + */ + public function testHandleClassNotFound($error, $translatedMessage, $autoloader = null) + { + if ($autoloader) { + // Unregister all autoloaders to ensure the custom provided + // autoloader is the only one to be used during the test run. + $autoloaders = spl_autoload_functions(); + array_map('spl_autoload_unregister', $autoloaders); + spl_autoload_register($autoloader); + } + + $handler = new ClassNotFoundFatalErrorHandler(); + + $exception = $handler->handleError($error, new FatalErrorException('', 0, $error['type'], $error['file'], $error['line'])); + + if ($autoloader) { + spl_autoload_unregister($autoloader); + array_map('spl_autoload_register', $autoloaders); + } + + $this->assertInstanceOf('Symfony\Component\Debug\Exception\ClassNotFoundException', $exception); + $this->assertSame($translatedMessage, $exception->getMessage()); + $this->assertSame($error['type'], $exception->getSeverity()); + $this->assertSame($error['file'], $exception->getFile()); + $this->assertSame($error['line'], $exception->getLine()); + } + + public function provideClassNotFoundData() + { + $autoloader = new ComposerClassLoader(); + $autoloader->add('Symfony\Component\Debug\Exception\\', realpath(__DIR__.'/../../Exception')); + + $debugClassLoader = new DebugClassLoader(array($autoloader, 'loadClass')); + + return array( + array( + array( + 'type' => 1, + 'line' => 12, + 'file' => 'foo.php', + 'message' => 'Class \'WhizBangFactory\' not found', + ), + "Attempted to load class \"WhizBangFactory\" from the global namespace.\nDid you forget a \"use\" statement?", + ), + array( + array( + 'type' => 1, + 'line' => 12, + 'file' => 'foo.php', + 'message' => 'Class \'Foo\\Bar\\WhizBangFactory\' not found', + ), + "Attempted to load class \"WhizBangFactory\" from namespace \"Foo\\Bar\".\nDid you forget a \"use\" statement for another namespace?", + ), + array( + array( + 'type' => 1, + 'line' => 12, + 'file' => 'foo.php', + 'message' => 'Class \'UndefinedFunctionException\' not found', + ), + "Attempted to load class \"UndefinedFunctionException\" from the global namespace.\nDid you forget a \"use\" statement for \"Symfony\Component\Debug\Exception\UndefinedFunctionException\"?", + ), + array( + array( + 'type' => 1, + 'line' => 12, + 'file' => 'foo.php', + 'message' => 'Class \'PEARClass\' not found', + ), + "Attempted to load class \"PEARClass\" from the global namespace.\nDid you forget a \"use\" statement for \"Symfony_Component_Debug_Tests_Fixtures_PEARClass\"?", + ), + array( + array( + 'type' => 1, + 'line' => 12, + 'file' => 'foo.php', + 'message' => 'Class \'Foo\\Bar\\UndefinedFunctionException\' not found', + ), + "Attempted to load class \"UndefinedFunctionException\" from namespace \"Foo\Bar\".\nDid you forget a \"use\" statement for \"Symfony\Component\Debug\Exception\UndefinedFunctionException\"?", + ), + array( + array( + 'type' => 1, + 'line' => 12, + 'file' => 'foo.php', + 'message' => 'Class \'Foo\\Bar\\UndefinedFunctionException\' not found', + ), + "Attempted to load class \"UndefinedFunctionException\" from namespace \"Foo\Bar\".\nDid you forget a \"use\" statement for \"Symfony\Component\Debug\Exception\UndefinedFunctionException\"?", + array($autoloader, 'loadClass'), + ), + array( + array( + 'type' => 1, + 'line' => 12, + 'file' => 'foo.php', + 'message' => 'Class \'Foo\\Bar\\UndefinedFunctionException\' not found', + ), + "Attempted to load class \"UndefinedFunctionException\" from namespace \"Foo\Bar\".\nDid you forget a \"use\" statement for \"Symfony\Component\Debug\Exception\UndefinedFunctionException\"?", + array($debugClassLoader, 'loadClass'), + ), + array( + array( + 'type' => 1, + 'line' => 12, + 'file' => 'foo.php', + 'message' => 'Class \'Foo\\Bar\\UndefinedFunctionException\' not found', + ), + "Attempted to load class \"UndefinedFunctionException\" from namespace \"Foo\\Bar\".\nDid you forget a \"use\" statement for another namespace?", + function ($className) { /* do nothing here */ }, + ), + ); + } + + public function testCannotRedeclareClass() + { + if (!file_exists(__DIR__.'/../FIXTURES2/REQUIREDTWICE.PHP')) { + $this->markTestSkipped('Can only be run on case insensitive filesystems'); + } + + require_once __DIR__.'/../FIXTURES2/REQUIREDTWICE.PHP'; + + $error = array( + 'type' => 1, + 'line' => 12, + 'file' => 'foo.php', + 'message' => 'Class \'Foo\\Bar\\RequiredTwice\' not found', + ); + + $handler = new ClassNotFoundFatalErrorHandler(); + $exception = $handler->handleError($error, new FatalErrorException('', 0, $error['type'], $error['file'], $error['line'])); + + $this->assertInstanceOf('Symfony\Component\Debug\Exception\ClassNotFoundException', $exception); + } +} diff --git a/lib/silex/vendor/symfony/debug/Tests/FatalErrorHandler/UndefinedFunctionFatalErrorHandlerTest.php b/lib/silex/vendor/symfony/debug/Tests/FatalErrorHandler/UndefinedFunctionFatalErrorHandlerTest.php new file mode 100644 index 000000000..1dc212004 --- /dev/null +++ b/lib/silex/vendor/symfony/debug/Tests/FatalErrorHandler/UndefinedFunctionFatalErrorHandlerTest.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\Debug\Tests\FatalErrorHandler; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Debug\Exception\FatalErrorException; +use Symfony\Component\Debug\FatalErrorHandler\UndefinedFunctionFatalErrorHandler; + +class UndefinedFunctionFatalErrorHandlerTest extends TestCase +{ + /** + * @dataProvider provideUndefinedFunctionData + */ + public function testUndefinedFunction($error, $translatedMessage) + { + $handler = new UndefinedFunctionFatalErrorHandler(); + $exception = $handler->handleError($error, new FatalErrorException('', 0, $error['type'], $error['file'], $error['line'])); + + $this->assertInstanceOf('Symfony\Component\Debug\Exception\UndefinedFunctionException', $exception); + // class names are case insensitive and PHP/HHVM do not return the same + $this->assertSame(strtolower($translatedMessage), strtolower($exception->getMessage())); + $this->assertSame($error['type'], $exception->getSeverity()); + $this->assertSame($error['file'], $exception->getFile()); + $this->assertSame($error['line'], $exception->getLine()); + } + + public function provideUndefinedFunctionData() + { + return array( + array( + array( + 'type' => 1, + 'line' => 12, + 'file' => 'foo.php', + 'message' => 'Call to undefined function test_namespaced_function()', + ), + "Attempted to call function \"test_namespaced_function\" from the global namespace.\nDid you mean to call \"\\symfony\\component\\debug\\tests\\fatalerrorhandler\\test_namespaced_function\"?", + ), + array( + array( + 'type' => 1, + 'line' => 12, + 'file' => 'foo.php', + 'message' => 'Call to undefined function Foo\\Bar\\Baz\\test_namespaced_function()', + ), + "Attempted to call function \"test_namespaced_function\" from namespace \"Foo\\Bar\\Baz\".\nDid you mean to call \"\\symfony\\component\\debug\\tests\\fatalerrorhandler\\test_namespaced_function\"?", + ), + array( + array( + 'type' => 1, + 'line' => 12, + 'file' => 'foo.php', + 'message' => 'Call to undefined function foo()', + ), + 'Attempted to call function "foo" from the global namespace.', + ), + array( + array( + 'type' => 1, + 'line' => 12, + 'file' => 'foo.php', + 'message' => 'Call to undefined function Foo\\Bar\\Baz\\foo()', + ), + 'Attempted to call function "foo" from namespace "Foo\Bar\Baz".', + ), + ); + } +} + +function test_namespaced_function() +{ +} diff --git a/lib/silex/vendor/symfony/debug/Tests/FatalErrorHandler/UndefinedMethodFatalErrorHandlerTest.php b/lib/silex/vendor/symfony/debug/Tests/FatalErrorHandler/UndefinedMethodFatalErrorHandlerTest.php new file mode 100644 index 000000000..739e5b2b1 --- /dev/null +++ b/lib/silex/vendor/symfony/debug/Tests/FatalErrorHandler/UndefinedMethodFatalErrorHandlerTest.php @@ -0,0 +1,76 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Debug\Tests\FatalErrorHandler; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Debug\Exception\FatalErrorException; +use Symfony\Component\Debug\FatalErrorHandler\UndefinedMethodFatalErrorHandler; + +class UndefinedMethodFatalErrorHandlerTest extends TestCase +{ + /** + * @dataProvider provideUndefinedMethodData + */ + public function testUndefinedMethod($error, $translatedMessage) + { + $handler = new UndefinedMethodFatalErrorHandler(); + $exception = $handler->handleError($error, new FatalErrorException('', 0, $error['type'], $error['file'], $error['line'])); + + $this->assertInstanceOf('Symfony\Component\Debug\Exception\UndefinedMethodException', $exception); + $this->assertSame($translatedMessage, $exception->getMessage()); + $this->assertSame($error['type'], $exception->getSeverity()); + $this->assertSame($error['file'], $exception->getFile()); + $this->assertSame($error['line'], $exception->getLine()); + } + + public function provideUndefinedMethodData() + { + return array( + array( + array( + 'type' => 1, + 'line' => 12, + 'file' => 'foo.php', + 'message' => 'Call to undefined method SplObjectStorage::what()', + ), + 'Attempted to call an undefined method named "what" of class "SplObjectStorage".', + ), + array( + array( + 'type' => 1, + 'line' => 12, + 'file' => 'foo.php', + 'message' => 'Call to undefined method SplObjectStorage::walid()', + ), + "Attempted to call an undefined method named \"walid\" of class \"SplObjectStorage\".\nDid you mean to call \"valid\"?", + ), + array( + array( + 'type' => 1, + 'line' => 12, + 'file' => 'foo.php', + 'message' => 'Call to undefined method SplObjectStorage::offsetFet()', + ), + "Attempted to call an undefined method named \"offsetFet\" of class \"SplObjectStorage\".\nDid you mean to call e.g. \"offsetGet\", \"offsetSet\" or \"offsetUnset\"?", + ), + array( + array( + 'type' => 1, + 'message' => 'Call to undefined method class@anonymous::test()', + 'file' => '/home/possum/work/symfony/test.php', + 'line' => 11, + ), + 'Attempted to call an undefined method named "test" of class "class@anonymous".', + ), + ); + } +} diff --git a/lib/silex/vendor/symfony/debug/Tests/Fixtures/AnnotatedClass.php b/lib/silex/vendor/symfony/debug/Tests/Fixtures/AnnotatedClass.php new file mode 100644 index 000000000..dff9517d0 --- /dev/null +++ b/lib/silex/vendor/symfony/debug/Tests/Fixtures/AnnotatedClass.php @@ -0,0 +1,13 @@ +exception = $e; + } + + public function __toString() + { + try { + throw $this->exception; + } catch (\Exception $e) { + // Using user_error() here is on purpose so we do not forget + // that this alias also should work alongside with trigger_error(). + return user_error($e, E_USER_ERROR); + } + } +} diff --git a/lib/silex/vendor/symfony/debug/Tests/Fixtures/casemismatch.php b/lib/silex/vendor/symfony/debug/Tests/Fixtures/casemismatch.php new file mode 100644 index 000000000..691d660fd --- /dev/null +++ b/lib/silex/vendor/symfony/debug/Tests/Fixtures/casemismatch.php @@ -0,0 +1,7 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Debug; + +function headers_sent() +{ + return false; +} + +function header($str, $replace = true, $status = null) +{ + Tests\testHeader($str, $replace, $status); +} + +namespace Symfony\Component\Debug\Tests; + +function testHeader() +{ + static $headers = array(); + + if (!$h = func_get_args()) { + $h = $headers; + $headers = array(); + + return $h; + } + + $headers[] = func_get_args(); +} diff --git a/lib/silex/vendor/symfony/debug/Tests/MockExceptionHandler.php b/lib/silex/vendor/symfony/debug/Tests/MockExceptionHandler.php new file mode 100644 index 000000000..2d6ce564d --- /dev/null +++ b/lib/silex/vendor/symfony/debug/Tests/MockExceptionHandler.php @@ -0,0 +1,24 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Debug\Tests; + +use Symfony\Component\Debug\ExceptionHandler; + +class MockExceptionHandler extends ExceptionHandler +{ + public $e; + + public function handle(\Exception $e) + { + $this->e = $e; + } +} diff --git a/lib/silex/vendor/symfony/debug/Tests/phpt/debug_class_loader.phpt b/lib/silex/vendor/symfony/debug/Tests/phpt/debug_class_loader.phpt new file mode 100644 index 000000000..b9d3d7288 --- /dev/null +++ b/lib/silex/vendor/symfony/debug/Tests/phpt/debug_class_loader.phpt @@ -0,0 +1,26 @@ +--TEST-- +Test DebugClassLoader with previously loaded parents +--FILE-- + +--EXPECTF-- +The "Symfony\Component\Debug\Tests\Fixtures\FinalMethod::finalMethod()" method is considered final since version 3.3. It may change without further notice as of its next major version. You should not extend it from "Symfony\Component\Debug\Tests\Fixtures\ExtendedFinalMethod". diff --git a/lib/silex/vendor/symfony/debug/Tests/phpt/decorate_exception_hander.phpt b/lib/silex/vendor/symfony/debug/Tests/phpt/decorate_exception_hander.phpt new file mode 100644 index 000000000..7ce7b9dc6 --- /dev/null +++ b/lib/silex/vendor/symfony/debug/Tests/phpt/decorate_exception_hander.phpt @@ -0,0 +1,47 @@ +--TEST-- +Test catching fatal errors when handlers are nested +--FILE-- + +--EXPECTF-- +Fatal error: Class 'Symfony\Component\Debug\missing' not found in %s on line %d +object(Symfony\Component\Debug\Exception\ClassNotFoundException)#%d (8) { + ["message":protected]=> + string(131) "Attempted to load class "missing" from namespace "Symfony\Component\Debug". +Did you forget a "use" statement for another namespace?" + ["string":"Exception":private]=> + string(0) "" + ["code":protected]=> + int(0) + ["file":protected]=> + string(%d) "%s" + ["line":protected]=> + int(%d) + ["trace":"Exception":private]=> + array(0) { + } + ["previous":"Exception":private]=> + NULL + ["severity":protected]=> + int(1) +} diff --git a/lib/silex/vendor/symfony/debug/Tests/phpt/exception_rethrown.phpt b/lib/silex/vendor/symfony/debug/Tests/phpt/exception_rethrown.phpt new file mode 100644 index 000000000..9df0a65cf --- /dev/null +++ b/lib/silex/vendor/symfony/debug/Tests/phpt/exception_rethrown.phpt @@ -0,0 +1,35 @@ +--TEST-- +Test rethrowing in custom exception handler +--FILE-- +setDefaultLogger(new TestLogger()); +ini_set('display_errors', 1); + +throw new \Exception('foo'); +?> +--EXPECTF-- +Uncaught Exception: foo +123 +Fatal error: Uncaught %s:25 +Stack trace: +%a diff --git a/lib/silex/vendor/symfony/debug/Tests/phpt/fatal_with_nested_handlers.phpt b/lib/silex/vendor/symfony/debug/Tests/phpt/fatal_with_nested_handlers.phpt new file mode 100644 index 000000000..5c5245c06 --- /dev/null +++ b/lib/silex/vendor/symfony/debug/Tests/phpt/fatal_with_nested_handlers.phpt @@ -0,0 +1,42 @@ +--TEST-- +Test catching fatal errors when handlers are nested +--FILE-- +setExceptionHandler('print_r'); + +if (true) { + class Broken implements \Serializable + { + } +} + +?> +--EXPECTF-- +array(1) { + [0]=> + string(37) "Error and exception handlers do match" +} +object(Symfony\Component\Debug\Exception\FatalErrorException)#%d (%d) { + ["message":protected]=> + string(199) "Error: Class Symfony\Component\Debug\Broken contains 2 abstract methods and must therefore be declared abstract or implement the remaining methods (Serializable::serialize, Serializable::unserialize)" +%a +} diff --git a/lib/silex/vendor/symfony/debug/composer.json b/lib/silex/vendor/symfony/debug/composer.json new file mode 100644 index 000000000..f98a5d07b --- /dev/null +++ b/lib/silex/vendor/symfony/debug/composer.json @@ -0,0 +1,40 @@ +{ + "name": "symfony/debug", + "type": "library", + "description": "Symfony Debug Component", + "keywords": [], + "homepage": "https://symfony.com", + "license": "MIT", + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "require": { + "php": "^5.5.9|>=7.0.8", + "psr/log": "~1.0" + }, + "conflict": { + "symfony/http-kernel": ">=2.3,<2.3.24|~2.4.0|>=2.5,<2.5.9|>=2.6,<2.6.2" + }, + "require-dev": { + "symfony/http-kernel": "~2.8|~3.0|~4.0" + }, + "autoload": { + "psr-4": { "Symfony\\Component\\Debug\\": "" }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "minimum-stability": "dev", + "extra": { + "branch-alias": { + "dev-master": "3.4-dev" + } + } +} diff --git a/lib/silex/vendor/symfony/debug/phpunit.xml.dist b/lib/silex/vendor/symfony/debug/phpunit.xml.dist new file mode 100644 index 000000000..12e58612b --- /dev/null +++ b/lib/silex/vendor/symfony/debug/phpunit.xml.dist @@ -0,0 +1,33 @@ + + + + + + + + + + ./Tests/ + + + ./Resources/ext/tests/ + + + + + + ./ + + ./Tests + ./vendor + + + + diff --git a/lib/silex/vendor/symfony/event-dispatcher/.gitignore b/lib/silex/vendor/symfony/event-dispatcher/.gitignore new file mode 100644 index 000000000..c49a5d8df --- /dev/null +++ b/lib/silex/vendor/symfony/event-dispatcher/.gitignore @@ -0,0 +1,3 @@ +vendor/ +composer.lock +phpunit.xml diff --git a/lib/silex/vendor/symfony/event-dispatcher/CHANGELOG.md b/lib/silex/vendor/symfony/event-dispatcher/CHANGELOG.md new file mode 100644 index 000000000..c6aa5389a --- /dev/null +++ b/lib/silex/vendor/symfony/event-dispatcher/CHANGELOG.md @@ -0,0 +1,42 @@ +CHANGELOG +========= + +3.4.0 +----- + + * Implementing `TraceableEventDispatcherInterface` without the `reset()` method has been deprecated. + +3.3.0 +----- + + * The ContainerAwareEventDispatcher class has been deprecated. Use EventDispatcher with closure factories instead. + +3.0.0 +----- + + * The method `getListenerPriority($eventName, $listener)` has been added to the + `EventDispatcherInterface`. + * The methods `Event::setDispatcher()`, `Event::getDispatcher()`, `Event::setName()` + and `Event::getName()` have been removed. + The event dispatcher and the event name are passed to the listener call. + +2.5.0 +----- + + * added Debug\TraceableEventDispatcher (originally in HttpKernel) + * changed Debug\TraceableEventDispatcherInterface to extend EventDispatcherInterface + * added RegisterListenersPass (originally in HttpKernel) + +2.1.0 +----- + + * added TraceableEventDispatcherInterface + * added ContainerAwareEventDispatcher + * added a reference to the EventDispatcher on the Event + * added a reference to the Event name on the event + * added fluid interface to the dispatch() method which now returns the Event + object + * added GenericEvent event class + * added the possibility for subscribers to subscribe several times for the + same event + * added ImmutableEventDispatcher diff --git a/lib/silex/vendor/symfony/event-dispatcher/ContainerAwareEventDispatcher.php b/lib/silex/vendor/symfony/event-dispatcher/ContainerAwareEventDispatcher.php new file mode 100644 index 000000000..81a1ff7ca --- /dev/null +++ b/lib/silex/vendor/symfony/event-dispatcher/ContainerAwareEventDispatcher.php @@ -0,0 +1,197 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\EventDispatcher; + +use Symfony\Component\DependencyInjection\ContainerInterface; + +/** + * Lazily loads listeners and subscribers from the dependency injection + * container. + * + * @author Fabien Potencier + * @author Bernhard Schussek + * @author Jordan Alliot + * + * @deprecated since 3.3, to be removed in 4.0. Use EventDispatcher with closure factories instead. + */ +class ContainerAwareEventDispatcher extends EventDispatcher +{ + private $container; + + /** + * The service IDs of the event listeners and subscribers. + */ + private $listenerIds = array(); + + /** + * The services registered as listeners. + */ + private $listeners = array(); + + public function __construct(ContainerInterface $container) + { + $this->container = $container; + + $class = get_class($this); + if ($this instanceof \PHPUnit_Framework_MockObject_MockObject || $this instanceof \Prophecy\Doubler\DoubleInterface) { + $class = get_parent_class($class); + } + if (__CLASS__ !== $class) { + @trigger_error(sprintf('The %s class is deprecated since Symfony 3.3 and will be removed in 4.0. Use EventDispatcher with closure factories instead.', __CLASS__), E_USER_DEPRECATED); + } + } + + /** + * Adds a service as event listener. + * + * @param string $eventName Event for which the listener is added + * @param array $callback The service ID of the listener service & the method + * name that has to be called + * @param int $priority The higher this value, the earlier an event listener + * will be triggered in the chain. + * Defaults to 0. + * + * @throws \InvalidArgumentException + */ + public function addListenerService($eventName, $callback, $priority = 0) + { + @trigger_error(sprintf('The %s class is deprecated since Symfony 3.3 and will be removed in 4.0. Use EventDispatcher with closure factories instead.', __CLASS__), E_USER_DEPRECATED); + + if (!is_array($callback) || 2 !== count($callback)) { + throw new \InvalidArgumentException('Expected an array("service", "method") argument'); + } + + $this->listenerIds[$eventName][] = array($callback[0], $callback[1], $priority); + } + + public function removeListener($eventName, $listener) + { + $this->lazyLoad($eventName); + + if (isset($this->listenerIds[$eventName])) { + foreach ($this->listenerIds[$eventName] as $i => list($serviceId, $method)) { + $key = $serviceId.'.'.$method; + if (isset($this->listeners[$eventName][$key]) && $listener === array($this->listeners[$eventName][$key], $method)) { + unset($this->listeners[$eventName][$key]); + if (empty($this->listeners[$eventName])) { + unset($this->listeners[$eventName]); + } + unset($this->listenerIds[$eventName][$i]); + if (empty($this->listenerIds[$eventName])) { + unset($this->listenerIds[$eventName]); + } + } + } + } + + parent::removeListener($eventName, $listener); + } + + /** + * {@inheritdoc} + */ + public function hasListeners($eventName = null) + { + if (null === $eventName) { + return $this->listenerIds || $this->listeners || parent::hasListeners(); + } + + if (isset($this->listenerIds[$eventName])) { + return true; + } + + return parent::hasListeners($eventName); + } + + /** + * {@inheritdoc} + */ + public function getListeners($eventName = null) + { + if (null === $eventName) { + foreach ($this->listenerIds as $serviceEventName => $args) { + $this->lazyLoad($serviceEventName); + } + } else { + $this->lazyLoad($eventName); + } + + return parent::getListeners($eventName); + } + + /** + * {@inheritdoc} + */ + public function getListenerPriority($eventName, $listener) + { + $this->lazyLoad($eventName); + + return parent::getListenerPriority($eventName, $listener); + } + + /** + * Adds a service as event subscriber. + * + * @param string $serviceId The service ID of the subscriber service + * @param string $class The service's class name (which must implement EventSubscriberInterface) + */ + public function addSubscriberService($serviceId, $class) + { + @trigger_error(sprintf('The %s class is deprecated since Symfony 3.3 and will be removed in 4.0. Use EventDispatcher with closure factories instead.', __CLASS__), E_USER_DEPRECATED); + + foreach ($class::getSubscribedEvents() as $eventName => $params) { + if (is_string($params)) { + $this->listenerIds[$eventName][] = array($serviceId, $params, 0); + } elseif (is_string($params[0])) { + $this->listenerIds[$eventName][] = array($serviceId, $params[0], isset($params[1]) ? $params[1] : 0); + } else { + foreach ($params as $listener) { + $this->listenerIds[$eventName][] = array($serviceId, $listener[0], isset($listener[1]) ? $listener[1] : 0); + } + } + } + } + + public function getContainer() + { + @trigger_error('The '.__METHOD__.'() method is deprecated since Symfony 3.3 as its class will be removed in 4.0. Inject the container or the services you need in your listeners/subscribers instead.', E_USER_DEPRECATED); + + return $this->container; + } + + /** + * Lazily loads listeners for this event from the dependency injection + * container. + * + * @param string $eventName The name of the event to dispatch. The name of + * the event is the name of the method that is + * invoked on listeners. + */ + protected function lazyLoad($eventName) + { + if (isset($this->listenerIds[$eventName])) { + foreach ($this->listenerIds[$eventName] as list($serviceId, $method, $priority)) { + $listener = $this->container->get($serviceId); + + $key = $serviceId.'.'.$method; + if (!isset($this->listeners[$eventName][$key])) { + $this->addListener($eventName, array($listener, $method), $priority); + } elseif ($this->listeners[$eventName][$key] !== $listener) { + parent::removeListener($eventName, array($this->listeners[$eventName][$key], $method)); + $this->addListener($eventName, array($listener, $method), $priority); + } + + $this->listeners[$eventName][$key] = $listener; + } + } + } +} diff --git a/lib/silex/vendor/symfony/event-dispatcher/Debug/TraceableEventDispatcher.php b/lib/silex/vendor/symfony/event-dispatcher/Debug/TraceableEventDispatcher.php new file mode 100644 index 000000000..9b5c689ad --- /dev/null +++ b/lib/silex/vendor/symfony/event-dispatcher/Debug/TraceableEventDispatcher.php @@ -0,0 +1,322 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\EventDispatcher\Debug; + +use Symfony\Component\EventDispatcher\EventDispatcherInterface; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; +use Symfony\Component\EventDispatcher\Event; +use Symfony\Component\Stopwatch\Stopwatch; +use Psr\Log\LoggerInterface; + +/** + * Collects some data about event listeners. + * + * This event dispatcher delegates the dispatching to another one. + * + * @author Fabien Potencier + */ +class TraceableEventDispatcher implements TraceableEventDispatcherInterface +{ + protected $logger; + protected $stopwatch; + + private $called; + private $dispatcher; + private $wrappedListeners; + + public function __construct(EventDispatcherInterface $dispatcher, Stopwatch $stopwatch, LoggerInterface $logger = null) + { + $this->dispatcher = $dispatcher; + $this->stopwatch = $stopwatch; + $this->logger = $logger; + $this->called = array(); + $this->wrappedListeners = array(); + } + + /** + * {@inheritdoc} + */ + public function addListener($eventName, $listener, $priority = 0) + { + $this->dispatcher->addListener($eventName, $listener, $priority); + } + + /** + * {@inheritdoc} + */ + public function addSubscriber(EventSubscriberInterface $subscriber) + { + $this->dispatcher->addSubscriber($subscriber); + } + + /** + * {@inheritdoc} + */ + public function removeListener($eventName, $listener) + { + if (isset($this->wrappedListeners[$eventName])) { + foreach ($this->wrappedListeners[$eventName] as $index => $wrappedListener) { + if ($wrappedListener->getWrappedListener() === $listener) { + $listener = $wrappedListener; + unset($this->wrappedListeners[$eventName][$index]); + break; + } + } + } + + return $this->dispatcher->removeListener($eventName, $listener); + } + + /** + * {@inheritdoc} + */ + public function removeSubscriber(EventSubscriberInterface $subscriber) + { + return $this->dispatcher->removeSubscriber($subscriber); + } + + /** + * {@inheritdoc} + */ + public function getListeners($eventName = null) + { + return $this->dispatcher->getListeners($eventName); + } + + /** + * {@inheritdoc} + */ + public function getListenerPriority($eventName, $listener) + { + // we might have wrapped listeners for the event (if called while dispatching) + // in that case get the priority by wrapper + if (isset($this->wrappedListeners[$eventName])) { + foreach ($this->wrappedListeners[$eventName] as $index => $wrappedListener) { + if ($wrappedListener->getWrappedListener() === $listener) { + return $this->dispatcher->getListenerPriority($eventName, $wrappedListener); + } + } + } + + return $this->dispatcher->getListenerPriority($eventName, $listener); + } + + /** + * {@inheritdoc} + */ + public function hasListeners($eventName = null) + { + return $this->dispatcher->hasListeners($eventName); + } + + /** + * {@inheritdoc} + */ + public function dispatch($eventName, Event $event = null) + { + if (null === $event) { + $event = new Event(); + } + + if (null !== $this->logger && $event->isPropagationStopped()) { + $this->logger->debug(sprintf('The "%s" event is already stopped. No listeners have been called.', $eventName)); + } + + $this->preProcess($eventName); + $this->preDispatch($eventName, $event); + + $e = $this->stopwatch->start($eventName, 'section'); + + $this->dispatcher->dispatch($eventName, $event); + + if ($e->isStarted()) { + $e->stop(); + } + + $this->postDispatch($eventName, $event); + $this->postProcess($eventName); + + return $event; + } + + /** + * {@inheritdoc} + */ + public function getCalledListeners() + { + $called = array(); + foreach ($this->called as $eventName => $listeners) { + foreach ($listeners as $listener) { + $called[$eventName.'.'.$listener->getPretty()] = $listener->getInfo($eventName); + } + } + + return $called; + } + + /** + * {@inheritdoc} + */ + public function getNotCalledListeners() + { + try { + $allListeners = $this->getListeners(); + } catch (\Exception $e) { + if (null !== $this->logger) { + $this->logger->info('An exception was thrown while getting the uncalled listeners.', array('exception' => $e)); + } + + // unable to retrieve the uncalled listeners + return array(); + } + + $notCalled = array(); + foreach ($allListeners as $eventName => $listeners) { + foreach ($listeners as $listener) { + $called = false; + if (isset($this->called[$eventName])) { + foreach ($this->called[$eventName] as $l) { + if ($l->getWrappedListener() === $listener) { + $called = true; + + break; + } + } + } + + if (!$called) { + if (!$listener instanceof WrappedListener) { + $listener = new WrappedListener($listener, null, $this->stopwatch, $this); + } + $notCalled[$eventName.'.'.$listener->getPretty()] = $listener->getInfo($eventName); + } + } + } + + uasort($notCalled, array($this, 'sortListenersByPriority')); + + return $notCalled; + } + + public function reset() + { + $this->called = array(); + } + + /** + * Proxies all method calls to the original event dispatcher. + * + * @param string $method The method name + * @param array $arguments The method arguments + * + * @return mixed + */ + public function __call($method, $arguments) + { + return call_user_func_array(array($this->dispatcher, $method), $arguments); + } + + /** + * Called before dispatching the event. + * + * @param string $eventName The event name + * @param Event $event The event + */ + protected function preDispatch($eventName, Event $event) + { + } + + /** + * Called after dispatching the event. + * + * @param string $eventName The event name + * @param Event $event The event + */ + protected function postDispatch($eventName, Event $event) + { + } + + private function preProcess($eventName) + { + foreach ($this->dispatcher->getListeners($eventName) as $listener) { + $priority = $this->getListenerPriority($eventName, $listener); + $wrappedListener = new WrappedListener($listener, null, $this->stopwatch, $this); + $this->wrappedListeners[$eventName][] = $wrappedListener; + $this->dispatcher->removeListener($eventName, $listener); + $this->dispatcher->addListener($eventName, $wrappedListener, $priority); + } + } + + private function postProcess($eventName) + { + unset($this->wrappedListeners[$eventName]); + $skipped = false; + foreach ($this->dispatcher->getListeners($eventName) as $listener) { + if (!$listener instanceof WrappedListener) { // #12845: a new listener was added during dispatch. + continue; + } + // Unwrap listener + $priority = $this->getListenerPriority($eventName, $listener); + $this->dispatcher->removeListener($eventName, $listener); + $this->dispatcher->addListener($eventName, $listener->getWrappedListener(), $priority); + + if (null !== $this->logger) { + $context = array('event' => $eventName, 'listener' => $listener->getPretty()); + } + + if ($listener->wasCalled()) { + if (null !== $this->logger) { + $this->logger->debug('Notified event "{event}" to listener "{listener}".', $context); + } + + if (!isset($this->called[$eventName])) { + $this->called[$eventName] = new \SplObjectStorage(); + } + + $this->called[$eventName]->attach($listener); + } + + if (null !== $this->logger && $skipped) { + $this->logger->debug('Listener "{listener}" was not called for event "{event}".', $context); + } + + if ($listener->stoppedPropagation()) { + if (null !== $this->logger) { + $this->logger->debug('Listener "{listener}" stopped propagation of the event "{event}".', $context); + } + + $skipped = true; + } + } + } + + private function sortListenersByPriority($a, $b) + { + if (is_int($a['priority']) && !is_int($b['priority'])) { + return 1; + } + + if (!is_int($a['priority']) && is_int($b['priority'])) { + return -1; + } + + if ($a['priority'] === $b['priority']) { + return 0; + } + + if ($a['priority'] > $b['priority']) { + return -1; + } + + return 1; + } +} diff --git a/lib/silex/vendor/symfony/event-dispatcher/Debug/TraceableEventDispatcherInterface.php b/lib/silex/vendor/symfony/event-dispatcher/Debug/TraceableEventDispatcherInterface.php new file mode 100644 index 000000000..f0212753b --- /dev/null +++ b/lib/silex/vendor/symfony/event-dispatcher/Debug/TraceableEventDispatcherInterface.php @@ -0,0 +1,36 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\EventDispatcher\Debug; + +use Symfony\Component\EventDispatcher\EventDispatcherInterface; + +/** + * @author Fabien Potencier + * + * @method reset() Resets the trace. + */ +interface TraceableEventDispatcherInterface extends EventDispatcherInterface +{ + /** + * Gets the called listeners. + * + * @return array An array of called listeners + */ + public function getCalledListeners(); + + /** + * Gets the not called listeners. + * + * @return array An array of not called listeners + */ + public function getNotCalledListeners(); +} diff --git a/lib/silex/vendor/symfony/event-dispatcher/Debug/WrappedListener.php b/lib/silex/vendor/symfony/event-dispatcher/Debug/WrappedListener.php new file mode 100644 index 000000000..f7b0273e1 --- /dev/null +++ b/lib/silex/vendor/symfony/event-dispatcher/Debug/WrappedListener.php @@ -0,0 +1,114 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\EventDispatcher\Debug; + +use Symfony\Component\Stopwatch\Stopwatch; +use Symfony\Component\EventDispatcher\Event; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; +use Symfony\Component\VarDumper\Caster\ClassStub; + +/** + * @author Fabien Potencier + */ +class WrappedListener +{ + private $listener; + private $name; + private $called; + private $stoppedPropagation; + private $stopwatch; + private $dispatcher; + private $pretty; + private $stub; + private static $hasClassStub; + + public function __construct($listener, $name, Stopwatch $stopwatch, EventDispatcherInterface $dispatcher = null) + { + $this->listener = $listener; + $this->name = $name; + $this->stopwatch = $stopwatch; + $this->dispatcher = $dispatcher; + $this->called = false; + $this->stoppedPropagation = false; + + if (is_array($listener)) { + $this->name = is_object($listener[0]) ? get_class($listener[0]) : $listener[0]; + $this->pretty = $this->name.'::'.$listener[1]; + } elseif ($listener instanceof \Closure) { + $this->pretty = $this->name = 'closure'; + } elseif (is_string($listener)) { + $this->pretty = $this->name = $listener; + } else { + $this->name = get_class($listener); + $this->pretty = $this->name.'::__invoke'; + } + + if (null !== $name) { + $this->name = $name; + } + + if (null === self::$hasClassStub) { + self::$hasClassStub = class_exists(ClassStub::class); + } + } + + public function getWrappedListener() + { + return $this->listener; + } + + public function wasCalled() + { + return $this->called; + } + + public function stoppedPropagation() + { + return $this->stoppedPropagation; + } + + public function getPretty() + { + return $this->pretty; + } + + public function getInfo($eventName) + { + if (null === $this->stub) { + $this->stub = self::$hasClassStub ? new ClassStub($this->pretty.'()', $this->listener) : $this->pretty.'()'; + } + + return array( + 'event' => $eventName, + 'priority' => null !== $this->dispatcher ? $this->dispatcher->getListenerPriority($eventName, $this->listener) : null, + 'pretty' => $this->pretty, + 'stub' => $this->stub, + ); + } + + public function __invoke(Event $event, $eventName, EventDispatcherInterface $dispatcher) + { + $this->called = true; + + $e = $this->stopwatch->start($this->name, 'event_listener'); + + call_user_func($this->listener, $event, $eventName, $this->dispatcher ?: $dispatcher); + + if ($e->isStarted()) { + $e->stop(); + } + + if ($event->isPropagationStopped()) { + $this->stoppedPropagation = true; + } + } +} diff --git a/lib/silex/vendor/symfony/event-dispatcher/DependencyInjection/RegisterListenersPass.php b/lib/silex/vendor/symfony/event-dispatcher/DependencyInjection/RegisterListenersPass.php new file mode 100644 index 000000000..9f9c09c52 --- /dev/null +++ b/lib/silex/vendor/symfony/event-dispatcher/DependencyInjection/RegisterListenersPass.php @@ -0,0 +1,139 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\EventDispatcher\DependencyInjection; + +use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\EventDispatcher\EventDispatcher; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; + +/** + * Compiler pass to register tagged services for an event dispatcher. + */ +class RegisterListenersPass implements CompilerPassInterface +{ + protected $dispatcherService; + protected $listenerTag; + protected $subscriberTag; + + private $hotPathEvents = array(); + private $hotPathTagName; + + /** + * @param string $dispatcherService Service name of the event dispatcher in processed container + * @param string $listenerTag Tag name used for listener + * @param string $subscriberTag Tag name used for subscribers + */ + public function __construct($dispatcherService = 'event_dispatcher', $listenerTag = 'kernel.event_listener', $subscriberTag = 'kernel.event_subscriber') + { + $this->dispatcherService = $dispatcherService; + $this->listenerTag = $listenerTag; + $this->subscriberTag = $subscriberTag; + } + + public function setHotPathEvents(array $hotPathEvents, $tagName = 'container.hot_path') + { + $this->hotPathEvents = array_flip($hotPathEvents); + $this->hotPathTagName = $tagName; + + return $this; + } + + public function process(ContainerBuilder $container) + { + if (!$container->hasDefinition($this->dispatcherService) && !$container->hasAlias($this->dispatcherService)) { + return; + } + + $definition = $container->findDefinition($this->dispatcherService); + + foreach ($container->findTaggedServiceIds($this->listenerTag, true) as $id => $events) { + foreach ($events as $event) { + $priority = isset($event['priority']) ? $event['priority'] : 0; + + if (!isset($event['event'])) { + throw new InvalidArgumentException(sprintf('Service "%s" must define the "event" attribute on "%s" tags.', $id, $this->listenerTag)); + } + + if (!isset($event['method'])) { + $event['method'] = 'on'.preg_replace_callback(array( + '/(?<=\b)[a-z]/i', + '/[^a-z0-9]/i', + ), function ($matches) { return strtoupper($matches[0]); }, $event['event']); + $event['method'] = preg_replace('/[^a-z0-9]/i', '', $event['method']); + } + + $definition->addMethodCall('addListener', array($event['event'], array(new ServiceClosureArgument(new Reference($id)), $event['method']), $priority)); + + if (isset($this->hotPathEvents[$event['event']])) { + $container->getDefinition($id)->addTag($this->hotPathTagName); + } + } + } + + $extractingDispatcher = new ExtractingEventDispatcher(); + + foreach ($container->findTaggedServiceIds($this->subscriberTag, true) as $id => $attributes) { + $def = $container->getDefinition($id); + + // We must assume that the class value has been correctly filled, even if the service is created by a factory + $class = $container->getParameterBag()->resolveValue($def->getClass()); + $interface = 'Symfony\Component\EventDispatcher\EventSubscriberInterface'; + + if (!is_subclass_of($class, $interface)) { + if (!class_exists($class, false)) { + throw new InvalidArgumentException(sprintf('Class "%s" used for service "%s" cannot be found.', $class, $id)); + } + + throw new InvalidArgumentException(sprintf('Service "%s" must implement interface "%s".', $id, $interface)); + } + $container->addObjectResource($class); + + ExtractingEventDispatcher::$subscriber = $class; + $extractingDispatcher->addSubscriber($extractingDispatcher); + foreach ($extractingDispatcher->listeners as $args) { + $args[1] = array(new ServiceClosureArgument(new Reference($id)), $args[1]); + $definition->addMethodCall('addListener', $args); + + if (isset($this->hotPathEvents[$args[0]])) { + $container->getDefinition($id)->addTag('container.hot_path'); + } + } + $extractingDispatcher->listeners = array(); + } + } +} + +/** + * @internal + */ +class ExtractingEventDispatcher extends EventDispatcher implements EventSubscriberInterface +{ + public $listeners = array(); + + public static $subscriber; + + public function addListener($eventName, $listener, $priority = 0) + { + $this->listeners[] = array($eventName, $listener[1], $priority); + } + + public static function getSubscribedEvents() + { + $callback = array(self::$subscriber, 'getSubscribedEvents'); + + return $callback(); + } +} diff --git a/lib/silex/vendor/symfony/event-dispatcher/Event.php b/lib/silex/vendor/symfony/event-dispatcher/Event.php new file mode 100644 index 000000000..9c56b2f55 --- /dev/null +++ b/lib/silex/vendor/symfony/event-dispatcher/Event.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\EventDispatcher; + +/** + * Event is the base class for classes containing event data. + * + * This class contains no event data. It is used by events that do not pass + * state information to an event handler when an event is raised. + * + * You can call the method stopPropagation() to abort the execution of + * further listeners in your event listener. + * + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @author Bernhard Schussek + */ +class Event +{ + /** + * @var bool Whether no further event listeners should be triggered + */ + private $propagationStopped = false; + + /** + * Returns whether further event listeners should be triggered. + * + * @see Event::stopPropagation() + * + * @return bool Whether propagation was already stopped for this event + */ + public function isPropagationStopped() + { + return $this->propagationStopped; + } + + /** + * Stops the propagation of the event to further event listeners. + * + * If multiple event listeners are connected to the same event, no + * further event listener will be triggered once any trigger calls + * stopPropagation(). + */ + public function stopPropagation() + { + $this->propagationStopped = true; + } +} diff --git a/lib/silex/vendor/symfony/event-dispatcher/EventDispatcher.php b/lib/silex/vendor/symfony/event-dispatcher/EventDispatcher.php new file mode 100644 index 000000000..bc79a958d --- /dev/null +++ b/lib/silex/vendor/symfony/event-dispatcher/EventDispatcher.php @@ -0,0 +1,236 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\EventDispatcher; + +/** + * The EventDispatcherInterface is the central point of Symfony's event listener system. + * + * Listeners are registered on the manager and events are dispatched through the + * manager. + * + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @author Bernhard Schussek + * @author Fabien Potencier + * @author Jordi Boggiano + * @author Jordan Alliot + * @author Nicolas Grekas + */ +class EventDispatcher implements EventDispatcherInterface +{ + private $listeners = array(); + private $sorted = array(); + + /** + * {@inheritdoc} + */ + public function dispatch($eventName, Event $event = null) + { + if (null === $event) { + $event = new Event(); + } + + if ($listeners = $this->getListeners($eventName)) { + $this->doDispatch($listeners, $eventName, $event); + } + + return $event; + } + + /** + * {@inheritdoc} + */ + public function getListeners($eventName = null) + { + if (null !== $eventName) { + if (empty($this->listeners[$eventName])) { + return array(); + } + + if (!isset($this->sorted[$eventName])) { + $this->sortListeners($eventName); + } + + return $this->sorted[$eventName]; + } + + foreach ($this->listeners as $eventName => $eventListeners) { + if (!isset($this->sorted[$eventName])) { + $this->sortListeners($eventName); + } + } + + return array_filter($this->sorted); + } + + /** + * {@inheritdoc} + */ + public function getListenerPriority($eventName, $listener) + { + if (empty($this->listeners[$eventName])) { + return; + } + + if (is_array($listener) && isset($listener[0]) && $listener[0] instanceof \Closure) { + $listener[0] = $listener[0](); + } + + foreach ($this->listeners[$eventName] as $priority => $listeners) { + foreach ($listeners as $k => $v) { + if ($v !== $listener && is_array($v) && isset($v[0]) && $v[0] instanceof \Closure) { + $v[0] = $v[0](); + $this->listeners[$eventName][$priority][$k] = $v; + } + if ($v === $listener) { + return $priority; + } + } + } + } + + /** + * {@inheritdoc} + */ + public function hasListeners($eventName = null) + { + if (null !== $eventName) { + return !empty($this->listeners[$eventName]); + } + + foreach ($this->listeners as $eventListeners) { + if ($eventListeners) { + return true; + } + } + + return false; + } + + /** + * {@inheritdoc} + */ + public function addListener($eventName, $listener, $priority = 0) + { + $this->listeners[$eventName][$priority][] = $listener; + unset($this->sorted[$eventName]); + } + + /** + * {@inheritdoc} + */ + public function removeListener($eventName, $listener) + { + if (empty($this->listeners[$eventName])) { + return; + } + + if (is_array($listener) && isset($listener[0]) && $listener[0] instanceof \Closure) { + $listener[0] = $listener[0](); + } + + foreach ($this->listeners[$eventName] as $priority => $listeners) { + foreach ($listeners as $k => $v) { + if ($v !== $listener && is_array($v) && isset($v[0]) && $v[0] instanceof \Closure) { + $v[0] = $v[0](); + } + if ($v === $listener) { + unset($listeners[$k], $this->sorted[$eventName]); + } else { + $listeners[$k] = $v; + } + } + + if ($listeners) { + $this->listeners[$eventName][$priority] = $listeners; + } else { + unset($this->listeners[$eventName][$priority]); + } + } + } + + /** + * {@inheritdoc} + */ + public function addSubscriber(EventSubscriberInterface $subscriber) + { + foreach ($subscriber->getSubscribedEvents() as $eventName => $params) { + if (is_string($params)) { + $this->addListener($eventName, array($subscriber, $params)); + } elseif (is_string($params[0])) { + $this->addListener($eventName, array($subscriber, $params[0]), isset($params[1]) ? $params[1] : 0); + } else { + foreach ($params as $listener) { + $this->addListener($eventName, array($subscriber, $listener[0]), isset($listener[1]) ? $listener[1] : 0); + } + } + } + } + + /** + * {@inheritdoc} + */ + public function removeSubscriber(EventSubscriberInterface $subscriber) + { + foreach ($subscriber->getSubscribedEvents() as $eventName => $params) { + if (is_array($params) && is_array($params[0])) { + foreach ($params as $listener) { + $this->removeListener($eventName, array($subscriber, $listener[0])); + } + } else { + $this->removeListener($eventName, array($subscriber, is_string($params) ? $params : $params[0])); + } + } + } + + /** + * Triggers the listeners of an event. + * + * This method can be overridden to add functionality that is executed + * for each listener. + * + * @param callable[] $listeners The event listeners + * @param string $eventName The name of the event to dispatch + * @param Event $event The event object to pass to the event handlers/listeners + */ + protected function doDispatch($listeners, $eventName, Event $event) + { + foreach ($listeners as $listener) { + if ($event->isPropagationStopped()) { + break; + } + \call_user_func($listener, $event, $eventName, $this); + } + } + + /** + * Sorts the internal list of listeners for the given event by priority. + * + * @param string $eventName The name of the event + */ + private function sortListeners($eventName) + { + krsort($this->listeners[$eventName]); + $this->sorted[$eventName] = array(); + + foreach ($this->listeners[$eventName] as $priority => $listeners) { + foreach ($listeners as $k => $listener) { + if (\is_array($listener) && isset($listener[0]) && $listener[0] instanceof \Closure) { + $listener[0] = $listener[0](); + $this->listeners[$eventName][$priority][$k] = $listener; + } + $this->sorted[$eventName][] = $listener; + } + } + } +} diff --git a/lib/silex/vendor/symfony/event-dispatcher/EventDispatcherInterface.php b/lib/silex/vendor/symfony/event-dispatcher/EventDispatcherInterface.php new file mode 100644 index 000000000..d3d0cb8a4 --- /dev/null +++ b/lib/silex/vendor/symfony/event-dispatcher/EventDispatcherInterface.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\EventDispatcher; + +/** + * The EventDispatcherInterface is the central point of Symfony's event listener system. + * Listeners are registered on the manager and events are dispatched through the + * manager. + * + * @author Bernhard Schussek + */ +interface EventDispatcherInterface +{ + /** + * Dispatches an event to all registered listeners. + * + * @param string $eventName The name of the event to dispatch. The name of + * the event is the name of the method that is + * invoked on listeners. + * @param Event $event The event to pass to the event handlers/listeners + * If not supplied, an empty Event instance is created + * + * @return Event + */ + public function dispatch($eventName, Event $event = null); + + /** + * Adds an event listener that listens on the specified events. + * + * @param string $eventName The event to listen on + * @param callable $listener The listener + * @param int $priority The higher this value, the earlier an event + * listener will be triggered in the chain (defaults to 0) + */ + public function addListener($eventName, $listener, $priority = 0); + + /** + * Adds an event subscriber. + * + * The subscriber is asked for all the events he is + * interested in and added as a listener for these events. + */ + public function addSubscriber(EventSubscriberInterface $subscriber); + + /** + * Removes an event listener from the specified events. + * + * @param string $eventName The event to remove a listener from + * @param callable $listener The listener to remove + */ + public function removeListener($eventName, $listener); + + public function removeSubscriber(EventSubscriberInterface $subscriber); + + /** + * Gets the listeners of a specific event or all listeners sorted by descending priority. + * + * @param string $eventName The name of the event + * + * @return array The event listeners for the specified event, or all event listeners by event name + */ + public function getListeners($eventName = null); + + /** + * Gets the listener priority for a specific event. + * + * Returns null if the event or the listener does not exist. + * + * @param string $eventName The name of the event + * @param callable $listener The listener + * + * @return int|null The event listener priority + */ + public function getListenerPriority($eventName, $listener); + + /** + * Checks whether an event has any registered listeners. + * + * @param string $eventName The name of the event + * + * @return bool true if the specified event has any listeners, false otherwise + */ + public function hasListeners($eventName = null); +} diff --git a/lib/silex/vendor/symfony/event-dispatcher/EventSubscriberInterface.php b/lib/silex/vendor/symfony/event-dispatcher/EventSubscriberInterface.php new file mode 100644 index 000000000..8af778919 --- /dev/null +++ b/lib/silex/vendor/symfony/event-dispatcher/EventSubscriberInterface.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\EventDispatcher; + +/** + * An EventSubscriber knows himself what events he is interested in. + * If an EventSubscriber is added to an EventDispatcherInterface, the manager invokes + * {@link getSubscribedEvents} and registers the subscriber as a listener for all + * returned events. + * + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @author Bernhard Schussek + */ +interface EventSubscriberInterface +{ + /** + * Returns an array of event names this subscriber wants to listen to. + * + * The array keys are event names and the value can be: + * + * * The method name to call (priority defaults to 0) + * * An array composed of the method name to call and the priority + * * An array of arrays composed of the method names to call and respective + * priorities, or 0 if unset + * + * For instance: + * + * * array('eventName' => 'methodName') + * * array('eventName' => array('methodName', $priority)) + * * array('eventName' => array(array('methodName1', $priority), array('methodName2'))) + * + * @return array The event names to listen to + */ + public static function getSubscribedEvents(); +} diff --git a/lib/silex/vendor/symfony/event-dispatcher/GenericEvent.php b/lib/silex/vendor/symfony/event-dispatcher/GenericEvent.php new file mode 100644 index 000000000..95c99408d --- /dev/null +++ b/lib/silex/vendor/symfony/event-dispatcher/GenericEvent.php @@ -0,0 +1,175 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\EventDispatcher; + +/** + * Event encapsulation class. + * + * Encapsulates events thus decoupling the observer from the subject they encapsulate. + * + * @author Drak + */ +class GenericEvent extends Event implements \ArrayAccess, \IteratorAggregate +{ + protected $subject; + protected $arguments; + + /** + * Encapsulate an event with $subject and $args. + * + * @param mixed $subject The subject of the event, usually an object or a callable + * @param array $arguments Arguments to store in the event + */ + public function __construct($subject = null, array $arguments = array()) + { + $this->subject = $subject; + $this->arguments = $arguments; + } + + /** + * Getter for subject property. + * + * @return mixed $subject The observer subject + */ + public function getSubject() + { + return $this->subject; + } + + /** + * Get argument by key. + * + * @param string $key Key + * + * @return mixed Contents of array key + * + * @throws \InvalidArgumentException if key is not found + */ + public function getArgument($key) + { + if ($this->hasArgument($key)) { + return $this->arguments[$key]; + } + + throw new \InvalidArgumentException(sprintf('Argument "%s" not found.', $key)); + } + + /** + * Add argument to event. + * + * @param string $key Argument name + * @param mixed $value Value + * + * @return $this + */ + public function setArgument($key, $value) + { + $this->arguments[$key] = $value; + + return $this; + } + + /** + * Getter for all arguments. + * + * @return array + */ + public function getArguments() + { + return $this->arguments; + } + + /** + * Set args property. + * + * @param array $args Arguments + * + * @return $this + */ + public function setArguments(array $args = array()) + { + $this->arguments = $args; + + return $this; + } + + /** + * Has argument. + * + * @param string $key Key of arguments array + * + * @return bool + */ + public function hasArgument($key) + { + return array_key_exists($key, $this->arguments); + } + + /** + * ArrayAccess for argument getter. + * + * @param string $key Array key + * + * @return mixed + * + * @throws \InvalidArgumentException if key does not exist in $this->args + */ + public function offsetGet($key) + { + return $this->getArgument($key); + } + + /** + * ArrayAccess for argument setter. + * + * @param string $key Array key to set + * @param mixed $value Value + */ + public function offsetSet($key, $value) + { + $this->setArgument($key, $value); + } + + /** + * ArrayAccess for unset argument. + * + * @param string $key Array key + */ + public function offsetUnset($key) + { + if ($this->hasArgument($key)) { + unset($this->arguments[$key]); + } + } + + /** + * ArrayAccess has argument. + * + * @param string $key Array key + * + * @return bool + */ + public function offsetExists($key) + { + return $this->hasArgument($key); + } + + /** + * IteratorAggregate for iterating over the object like an array. + * + * @return \ArrayIterator + */ + public function getIterator() + { + return new \ArrayIterator($this->arguments); + } +} diff --git a/lib/silex/vendor/symfony/event-dispatcher/ImmutableEventDispatcher.php b/lib/silex/vendor/symfony/event-dispatcher/ImmutableEventDispatcher.php new file mode 100644 index 000000000..b3cf56c50 --- /dev/null +++ b/lib/silex/vendor/symfony/event-dispatcher/ImmutableEventDispatcher.php @@ -0,0 +1,91 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\EventDispatcher; + +/** + * A read-only proxy for an event dispatcher. + * + * @author Bernhard Schussek + */ +class ImmutableEventDispatcher implements EventDispatcherInterface +{ + private $dispatcher; + + public function __construct(EventDispatcherInterface $dispatcher) + { + $this->dispatcher = $dispatcher; + } + + /** + * {@inheritdoc} + */ + public function dispatch($eventName, Event $event = null) + { + return $this->dispatcher->dispatch($eventName, $event); + } + + /** + * {@inheritdoc} + */ + public function addListener($eventName, $listener, $priority = 0) + { + throw new \BadMethodCallException('Unmodifiable event dispatchers must not be modified.'); + } + + /** + * {@inheritdoc} + */ + public function addSubscriber(EventSubscriberInterface $subscriber) + { + throw new \BadMethodCallException('Unmodifiable event dispatchers must not be modified.'); + } + + /** + * {@inheritdoc} + */ + public function removeListener($eventName, $listener) + { + throw new \BadMethodCallException('Unmodifiable event dispatchers must not be modified.'); + } + + /** + * {@inheritdoc} + */ + public function removeSubscriber(EventSubscriberInterface $subscriber) + { + throw new \BadMethodCallException('Unmodifiable event dispatchers must not be modified.'); + } + + /** + * {@inheritdoc} + */ + public function getListeners($eventName = null) + { + return $this->dispatcher->getListeners($eventName); + } + + /** + * {@inheritdoc} + */ + public function getListenerPriority($eventName, $listener) + { + return $this->dispatcher->getListenerPriority($eventName, $listener); + } + + /** + * {@inheritdoc} + */ + public function hasListeners($eventName = null) + { + return $this->dispatcher->hasListeners($eventName); + } +} diff --git a/lib/silex/vendor/symfony/event-dispatcher/LICENSE b/lib/silex/vendor/symfony/event-dispatcher/LICENSE new file mode 100644 index 000000000..21d7fb9e2 --- /dev/null +++ b/lib/silex/vendor/symfony/event-dispatcher/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2004-2018 Fabien Potencier + +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/lib/silex/vendor/symfony/event-dispatcher/README.md b/lib/silex/vendor/symfony/event-dispatcher/README.md new file mode 100644 index 000000000..185c3fecf --- /dev/null +++ b/lib/silex/vendor/symfony/event-dispatcher/README.md @@ -0,0 +1,15 @@ +EventDispatcher Component +========================= + +The EventDispatcher component provides tools that allow your application +components to communicate with each other by dispatching events and listening to +them. + +Resources +--------- + + * [Documentation](https://symfony.com/doc/current/components/event_dispatcher/index.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/lib/silex/vendor/symfony/event-dispatcher/Tests/AbstractEventDispatcherTest.php b/lib/silex/vendor/symfony/event-dispatcher/Tests/AbstractEventDispatcherTest.php new file mode 100644 index 000000000..9443f2166 --- /dev/null +++ b/lib/silex/vendor/symfony/event-dispatcher/Tests/AbstractEventDispatcherTest.php @@ -0,0 +1,442 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\EventDispatcher\Tests; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\EventDispatcher\Event; +use Symfony\Component\EventDispatcher\EventDispatcher; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; + +abstract class AbstractEventDispatcherTest extends TestCase +{ + /* Some pseudo events */ + const preFoo = 'pre.foo'; + const postFoo = 'post.foo'; + const preBar = 'pre.bar'; + const postBar = 'post.bar'; + + /** + * @var EventDispatcher + */ + private $dispatcher; + + private $listener; + + protected function setUp() + { + $this->dispatcher = $this->createEventDispatcher(); + $this->listener = new TestEventListener(); + } + + protected function tearDown() + { + $this->dispatcher = null; + $this->listener = null; + } + + abstract protected function createEventDispatcher(); + + public function testInitialState() + { + $this->assertEquals(array(), $this->dispatcher->getListeners()); + $this->assertFalse($this->dispatcher->hasListeners(self::preFoo)); + $this->assertFalse($this->dispatcher->hasListeners(self::postFoo)); + } + + public function testAddListener() + { + $this->dispatcher->addListener('pre.foo', array($this->listener, 'preFoo')); + $this->dispatcher->addListener('post.foo', array($this->listener, 'postFoo')); + $this->assertTrue($this->dispatcher->hasListeners()); + $this->assertTrue($this->dispatcher->hasListeners(self::preFoo)); + $this->assertTrue($this->dispatcher->hasListeners(self::postFoo)); + $this->assertCount(1, $this->dispatcher->getListeners(self::preFoo)); + $this->assertCount(1, $this->dispatcher->getListeners(self::postFoo)); + $this->assertCount(2, $this->dispatcher->getListeners()); + } + + public function testGetListenersSortsByPriority() + { + $listener1 = new TestEventListener(); + $listener2 = new TestEventListener(); + $listener3 = new TestEventListener(); + $listener1->name = '1'; + $listener2->name = '2'; + $listener3->name = '3'; + + $this->dispatcher->addListener('pre.foo', array($listener1, 'preFoo'), -10); + $this->dispatcher->addListener('pre.foo', array($listener2, 'preFoo'), 10); + $this->dispatcher->addListener('pre.foo', array($listener3, 'preFoo')); + + $expected = array( + array($listener2, 'preFoo'), + array($listener3, 'preFoo'), + array($listener1, 'preFoo'), + ); + + $this->assertSame($expected, $this->dispatcher->getListeners('pre.foo')); + } + + public function testGetAllListenersSortsByPriority() + { + $listener1 = new TestEventListener(); + $listener2 = new TestEventListener(); + $listener3 = new TestEventListener(); + $listener4 = new TestEventListener(); + $listener5 = new TestEventListener(); + $listener6 = new TestEventListener(); + + $this->dispatcher->addListener('pre.foo', $listener1, -10); + $this->dispatcher->addListener('pre.foo', $listener2); + $this->dispatcher->addListener('pre.foo', $listener3, 10); + $this->dispatcher->addListener('post.foo', $listener4, -10); + $this->dispatcher->addListener('post.foo', $listener5); + $this->dispatcher->addListener('post.foo', $listener6, 10); + + $expected = array( + 'pre.foo' => array($listener3, $listener2, $listener1), + 'post.foo' => array($listener6, $listener5, $listener4), + ); + + $this->assertSame($expected, $this->dispatcher->getListeners()); + } + + public function testGetListenerPriority() + { + $listener1 = new TestEventListener(); + $listener2 = new TestEventListener(); + + $this->dispatcher->addListener('pre.foo', $listener1, -10); + $this->dispatcher->addListener('pre.foo', $listener2); + + $this->assertSame(-10, $this->dispatcher->getListenerPriority('pre.foo', $listener1)); + $this->assertSame(0, $this->dispatcher->getListenerPriority('pre.foo', $listener2)); + $this->assertNull($this->dispatcher->getListenerPriority('pre.bar', $listener2)); + $this->assertNull($this->dispatcher->getListenerPriority('pre.foo', function () {})); + } + + public function testDispatch() + { + $this->dispatcher->addListener('pre.foo', array($this->listener, 'preFoo')); + $this->dispatcher->addListener('post.foo', array($this->listener, 'postFoo')); + $this->dispatcher->dispatch(self::preFoo); + $this->assertTrue($this->listener->preFooInvoked); + $this->assertFalse($this->listener->postFooInvoked); + $this->assertInstanceOf('Symfony\Component\EventDispatcher\Event', $this->dispatcher->dispatch('noevent')); + $this->assertInstanceOf('Symfony\Component\EventDispatcher\Event', $this->dispatcher->dispatch(self::preFoo)); + $event = new Event(); + $return = $this->dispatcher->dispatch(self::preFoo, $event); + $this->assertSame($event, $return); + } + + public function testDispatchForClosure() + { + $invoked = 0; + $listener = function () use (&$invoked) { + ++$invoked; + }; + $this->dispatcher->addListener('pre.foo', $listener); + $this->dispatcher->addListener('post.foo', $listener); + $this->dispatcher->dispatch(self::preFoo); + $this->assertEquals(1, $invoked); + } + + public function testStopEventPropagation() + { + $otherListener = new TestEventListener(); + + // postFoo() stops the propagation, so only one listener should + // be executed + // Manually set priority to enforce $this->listener to be called first + $this->dispatcher->addListener('post.foo', array($this->listener, 'postFoo'), 10); + $this->dispatcher->addListener('post.foo', array($otherListener, 'preFoo')); + $this->dispatcher->dispatch(self::postFoo); + $this->assertTrue($this->listener->postFooInvoked); + $this->assertFalse($otherListener->postFooInvoked); + } + + public function testDispatchByPriority() + { + $invoked = array(); + $listener1 = function () use (&$invoked) { + $invoked[] = '1'; + }; + $listener2 = function () use (&$invoked) { + $invoked[] = '2'; + }; + $listener3 = function () use (&$invoked) { + $invoked[] = '3'; + }; + $this->dispatcher->addListener('pre.foo', $listener1, -10); + $this->dispatcher->addListener('pre.foo', $listener2); + $this->dispatcher->addListener('pre.foo', $listener3, 10); + $this->dispatcher->dispatch(self::preFoo); + $this->assertEquals(array('3', '2', '1'), $invoked); + } + + public function testRemoveListener() + { + $this->dispatcher->addListener('pre.bar', $this->listener); + $this->assertTrue($this->dispatcher->hasListeners(self::preBar)); + $this->dispatcher->removeListener('pre.bar', $this->listener); + $this->assertFalse($this->dispatcher->hasListeners(self::preBar)); + $this->dispatcher->removeListener('notExists', $this->listener); + } + + public function testAddSubscriber() + { + $eventSubscriber = new TestEventSubscriber(); + $this->dispatcher->addSubscriber($eventSubscriber); + $this->assertTrue($this->dispatcher->hasListeners(self::preFoo)); + $this->assertTrue($this->dispatcher->hasListeners(self::postFoo)); + } + + public function testAddSubscriberWithPriorities() + { + $eventSubscriber = new TestEventSubscriber(); + $this->dispatcher->addSubscriber($eventSubscriber); + + $eventSubscriber = new TestEventSubscriberWithPriorities(); + $this->dispatcher->addSubscriber($eventSubscriber); + + $listeners = $this->dispatcher->getListeners('pre.foo'); + $this->assertTrue($this->dispatcher->hasListeners(self::preFoo)); + $this->assertCount(2, $listeners); + $this->assertInstanceOf('Symfony\Component\EventDispatcher\Tests\TestEventSubscriberWithPriorities', $listeners[0][0]); + } + + public function testAddSubscriberWithMultipleListeners() + { + $eventSubscriber = new TestEventSubscriberWithMultipleListeners(); + $this->dispatcher->addSubscriber($eventSubscriber); + + $listeners = $this->dispatcher->getListeners('pre.foo'); + $this->assertTrue($this->dispatcher->hasListeners(self::preFoo)); + $this->assertCount(2, $listeners); + $this->assertEquals('preFoo2', $listeners[0][1]); + } + + public function testRemoveSubscriber() + { + $eventSubscriber = new TestEventSubscriber(); + $this->dispatcher->addSubscriber($eventSubscriber); + $this->assertTrue($this->dispatcher->hasListeners(self::preFoo)); + $this->assertTrue($this->dispatcher->hasListeners(self::postFoo)); + $this->dispatcher->removeSubscriber($eventSubscriber); + $this->assertFalse($this->dispatcher->hasListeners(self::preFoo)); + $this->assertFalse($this->dispatcher->hasListeners(self::postFoo)); + } + + public function testRemoveSubscriberWithPriorities() + { + $eventSubscriber = new TestEventSubscriberWithPriorities(); + $this->dispatcher->addSubscriber($eventSubscriber); + $this->assertTrue($this->dispatcher->hasListeners(self::preFoo)); + $this->dispatcher->removeSubscriber($eventSubscriber); + $this->assertFalse($this->dispatcher->hasListeners(self::preFoo)); + } + + public function testRemoveSubscriberWithMultipleListeners() + { + $eventSubscriber = new TestEventSubscriberWithMultipleListeners(); + $this->dispatcher->addSubscriber($eventSubscriber); + $this->assertTrue($this->dispatcher->hasListeners(self::preFoo)); + $this->assertCount(2, $this->dispatcher->getListeners(self::preFoo)); + $this->dispatcher->removeSubscriber($eventSubscriber); + $this->assertFalse($this->dispatcher->hasListeners(self::preFoo)); + } + + public function testEventReceivesTheDispatcherInstanceAsArgument() + { + $listener = new TestWithDispatcher(); + $this->dispatcher->addListener('test', array($listener, 'foo')); + $this->assertNull($listener->name); + $this->assertNull($listener->dispatcher); + $this->dispatcher->dispatch('test'); + $this->assertEquals('test', $listener->name); + $this->assertSame($this->dispatcher, $listener->dispatcher); + } + + /** + * @see https://bugs.php.net/bug.php?id=62976 + * + * This bug affects: + * - The PHP 5.3 branch for versions < 5.3.18 + * - The PHP 5.4 branch for versions < 5.4.8 + * - The PHP 5.5 branch is not affected + */ + public function testWorkaroundForPhpBug62976() + { + $dispatcher = $this->createEventDispatcher(); + $dispatcher->addListener('bug.62976', new CallableClass()); + $dispatcher->removeListener('bug.62976', function () {}); + $this->assertTrue($dispatcher->hasListeners('bug.62976')); + } + + public function testHasListenersWhenAddedCallbackListenerIsRemoved() + { + $listener = function () {}; + $this->dispatcher->addListener('foo', $listener); + $this->dispatcher->removeListener('foo', $listener); + $this->assertFalse($this->dispatcher->hasListeners()); + } + + public function testGetListenersWhenAddedCallbackListenerIsRemoved() + { + $listener = function () {}; + $this->dispatcher->addListener('foo', $listener); + $this->dispatcher->removeListener('foo', $listener); + $this->assertSame(array(), $this->dispatcher->getListeners()); + } + + public function testHasListenersWithoutEventsReturnsFalseAfterHasListenersWithEventHasBeenCalled() + { + $this->assertFalse($this->dispatcher->hasListeners('foo')); + $this->assertFalse($this->dispatcher->hasListeners()); + } + + public function testHasListenersIsLazy() + { + $called = 0; + $listener = array(function () use (&$called) { ++$called; }, 'onFoo'); + $this->dispatcher->addListener('foo', $listener); + $this->assertTrue($this->dispatcher->hasListeners()); + $this->assertTrue($this->dispatcher->hasListeners('foo')); + $this->assertSame(0, $called); + } + + public function testDispatchLazyListener() + { + $called = 0; + $factory = function () use (&$called) { + ++$called; + + return new TestWithDispatcher(); + }; + $this->dispatcher->addListener('foo', array($factory, 'foo')); + $this->assertSame(0, $called); + $this->dispatcher->dispatch('foo', new Event()); + $this->dispatcher->dispatch('foo', new Event()); + $this->assertSame(1, $called); + } + + public function testRemoveFindsLazyListeners() + { + $test = new TestWithDispatcher(); + $factory = function () use ($test) { return $test; }; + + $this->dispatcher->addListener('foo', array($factory, 'foo')); + $this->assertTrue($this->dispatcher->hasListeners('foo')); + $this->dispatcher->removeListener('foo', array($test, 'foo')); + $this->assertFalse($this->dispatcher->hasListeners('foo')); + + $this->dispatcher->addListener('foo', array($test, 'foo')); + $this->assertTrue($this->dispatcher->hasListeners('foo')); + $this->dispatcher->removeListener('foo', array($factory, 'foo')); + $this->assertFalse($this->dispatcher->hasListeners('foo')); + } + + public function testPriorityFindsLazyListeners() + { + $test = new TestWithDispatcher(); + $factory = function () use ($test) { return $test; }; + + $this->dispatcher->addListener('foo', array($factory, 'foo'), 3); + $this->assertSame(3, $this->dispatcher->getListenerPriority('foo', array($test, 'foo'))); + $this->dispatcher->removeListener('foo', array($factory, 'foo')); + + $this->dispatcher->addListener('foo', array($test, 'foo'), 5); + $this->assertSame(5, $this->dispatcher->getListenerPriority('foo', array($factory, 'foo'))); + } + + public function testGetLazyListeners() + { + $test = new TestWithDispatcher(); + $factory = function () use ($test) { return $test; }; + + $this->dispatcher->addListener('foo', array($factory, 'foo'), 3); + $this->assertSame(array(array($test, 'foo')), $this->dispatcher->getListeners('foo')); + + $this->dispatcher->removeListener('foo', array($test, 'foo')); + $this->dispatcher->addListener('bar', array($factory, 'foo'), 3); + $this->assertSame(array('bar' => array(array($test, 'foo'))), $this->dispatcher->getListeners()); + } +} + +class CallableClass +{ + public function __invoke() + { + } +} + +class TestEventListener +{ + public $preFooInvoked = false; + public $postFooInvoked = false; + + /* Listener methods */ + + public function preFoo(Event $e) + { + $this->preFooInvoked = true; + } + + public function postFoo(Event $e) + { + $this->postFooInvoked = true; + + $e->stopPropagation(); + } +} + +class TestWithDispatcher +{ + public $name; + public $dispatcher; + + public function foo(Event $e, $name, $dispatcher) + { + $this->name = $name; + $this->dispatcher = $dispatcher; + } +} + +class TestEventSubscriber implements EventSubscriberInterface +{ + public static function getSubscribedEvents() + { + return array('pre.foo' => 'preFoo', 'post.foo' => 'postFoo'); + } +} + +class TestEventSubscriberWithPriorities implements EventSubscriberInterface +{ + public static function getSubscribedEvents() + { + return array( + 'pre.foo' => array('preFoo', 10), + 'post.foo' => array('postFoo'), + ); + } +} + +class TestEventSubscriberWithMultipleListeners implements EventSubscriberInterface +{ + public static function getSubscribedEvents() + { + return array('pre.foo' => array( + array('preFoo1'), + array('preFoo2', 10), + )); + } +} diff --git a/lib/silex/vendor/symfony/event-dispatcher/Tests/ContainerAwareEventDispatcherTest.php b/lib/silex/vendor/symfony/event-dispatcher/Tests/ContainerAwareEventDispatcherTest.php new file mode 100644 index 000000000..9d5eecc54 --- /dev/null +++ b/lib/silex/vendor/symfony/event-dispatcher/Tests/ContainerAwareEventDispatcherTest.php @@ -0,0 +1,210 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\EventDispatcher\Tests; + +use Symfony\Component\DependencyInjection\Container; +use Symfony\Component\EventDispatcher\ContainerAwareEventDispatcher; +use Symfony\Component\EventDispatcher\Event; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; + +/** + * @group legacy + */ +class ContainerAwareEventDispatcherTest extends AbstractEventDispatcherTest +{ + protected function createEventDispatcher() + { + $container = new Container(); + + return new ContainerAwareEventDispatcher($container); + } + + public function testAddAListenerService() + { + $event = new Event(); + + $service = $this->getMockBuilder('Symfony\Component\EventDispatcher\Tests\Service')->getMock(); + + $service + ->expects($this->once()) + ->method('onEvent') + ->with($event) + ; + + $container = new Container(); + $container->set('service.listener', $service); + + $dispatcher = new ContainerAwareEventDispatcher($container); + $dispatcher->addListenerService('onEvent', array('service.listener', 'onEvent')); + + $dispatcher->dispatch('onEvent', $event); + } + + public function testAddASubscriberService() + { + $event = new Event(); + + $service = $this->getMockBuilder('Symfony\Component\EventDispatcher\Tests\SubscriberService')->getMock(); + + $service + ->expects($this->once()) + ->method('onEvent') + ->with($event) + ; + + $service + ->expects($this->once()) + ->method('onEventWithPriority') + ->with($event) + ; + + $service + ->expects($this->once()) + ->method('onEventNested') + ->with($event) + ; + + $container = new Container(); + $container->set('service.subscriber', $service); + + $dispatcher = new ContainerAwareEventDispatcher($container); + $dispatcher->addSubscriberService('service.subscriber', 'Symfony\Component\EventDispatcher\Tests\SubscriberService'); + + $dispatcher->dispatch('onEvent', $event); + $dispatcher->dispatch('onEventWithPriority', $event); + $dispatcher->dispatch('onEventNested', $event); + } + + public function testPreventDuplicateListenerService() + { + $event = new Event(); + + $service = $this->getMockBuilder('Symfony\Component\EventDispatcher\Tests\Service')->getMock(); + + $service + ->expects($this->once()) + ->method('onEvent') + ->with($event) + ; + + $container = new Container(); + $container->set('service.listener', $service); + + $dispatcher = new ContainerAwareEventDispatcher($container); + $dispatcher->addListenerService('onEvent', array('service.listener', 'onEvent'), 5); + $dispatcher->addListenerService('onEvent', array('service.listener', 'onEvent'), 10); + + $dispatcher->dispatch('onEvent', $event); + } + + public function testHasListenersOnLazyLoad() + { + $event = new Event(); + + $service = $this->getMockBuilder('Symfony\Component\EventDispatcher\Tests\Service')->getMock(); + + $container = new Container(); + $container->set('service.listener', $service); + + $dispatcher = new ContainerAwareEventDispatcher($container); + $dispatcher->addListenerService('onEvent', array('service.listener', 'onEvent')); + + $service + ->expects($this->once()) + ->method('onEvent') + ->with($event) + ; + + $this->assertTrue($dispatcher->hasListeners()); + + if ($dispatcher->hasListeners('onEvent')) { + $dispatcher->dispatch('onEvent'); + } + } + + public function testGetListenersOnLazyLoad() + { + $service = $this->getMockBuilder('Symfony\Component\EventDispatcher\Tests\Service')->getMock(); + + $container = new Container(); + $container->set('service.listener', $service); + + $dispatcher = new ContainerAwareEventDispatcher($container); + $dispatcher->addListenerService('onEvent', array('service.listener', 'onEvent')); + + $listeners = $dispatcher->getListeners(); + + $this->assertArrayHasKey('onEvent', $listeners); + + $this->assertCount(1, $dispatcher->getListeners('onEvent')); + } + + public function testRemoveAfterDispatch() + { + $service = $this->getMockBuilder('Symfony\Component\EventDispatcher\Tests\Service')->getMock(); + + $container = new Container(); + $container->set('service.listener', $service); + + $dispatcher = new ContainerAwareEventDispatcher($container); + $dispatcher->addListenerService('onEvent', array('service.listener', 'onEvent')); + + $dispatcher->dispatch('onEvent', new Event()); + $dispatcher->removeListener('onEvent', array($container->get('service.listener'), 'onEvent')); + $this->assertFalse($dispatcher->hasListeners('onEvent')); + } + + public function testRemoveBeforeDispatch() + { + $service = $this->getMockBuilder('Symfony\Component\EventDispatcher\Tests\Service')->getMock(); + + $container = new Container(); + $container->set('service.listener', $service); + + $dispatcher = new ContainerAwareEventDispatcher($container); + $dispatcher->addListenerService('onEvent', array('service.listener', 'onEvent')); + + $dispatcher->removeListener('onEvent', array($container->get('service.listener'), 'onEvent')); + $this->assertFalse($dispatcher->hasListeners('onEvent')); + } +} + +class Service +{ + public function onEvent(Event $e) + { + } +} + +class SubscriberService implements EventSubscriberInterface +{ + public static function getSubscribedEvents() + { + return array( + 'onEvent' => 'onEvent', + 'onEventWithPriority' => array('onEventWithPriority', 10), + 'onEventNested' => array(array('onEventNested')), + ); + } + + public function onEvent(Event $e) + { + } + + public function onEventWithPriority(Event $e) + { + } + + public function onEventNested(Event $e) + { + } +} diff --git a/lib/silex/vendor/symfony/event-dispatcher/Tests/Debug/TraceableEventDispatcherTest.php b/lib/silex/vendor/symfony/event-dispatcher/Tests/Debug/TraceableEventDispatcherTest.php new file mode 100644 index 000000000..53a3421af --- /dev/null +++ b/lib/silex/vendor/symfony/event-dispatcher/Tests/Debug/TraceableEventDispatcherTest.php @@ -0,0 +1,257 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\EventDispatcher\Tests\Debug; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\EventDispatcher\Debug\TraceableEventDispatcher; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; +use Symfony\Component\EventDispatcher\EventDispatcher; +use Symfony\Component\EventDispatcher\Event; +use Symfony\Component\Stopwatch\Stopwatch; + +class TraceableEventDispatcherTest extends TestCase +{ + public function testAddRemoveListener() + { + $dispatcher = new EventDispatcher(); + $tdispatcher = new TraceableEventDispatcher($dispatcher, new Stopwatch()); + + $tdispatcher->addListener('foo', $listener = function () {}); + $listeners = $dispatcher->getListeners('foo'); + $this->assertCount(1, $listeners); + $this->assertSame($listener, $listeners[0]); + + $tdispatcher->removeListener('foo', $listener); + $this->assertCount(0, $dispatcher->getListeners('foo')); + } + + public function testGetListeners() + { + $dispatcher = new EventDispatcher(); + $tdispatcher = new TraceableEventDispatcher($dispatcher, new Stopwatch()); + + $tdispatcher->addListener('foo', $listener = function () {}); + $this->assertSame($dispatcher->getListeners('foo'), $tdispatcher->getListeners('foo')); + } + + public function testHasListeners() + { + $dispatcher = new EventDispatcher(); + $tdispatcher = new TraceableEventDispatcher($dispatcher, new Stopwatch()); + + $this->assertFalse($dispatcher->hasListeners('foo')); + $this->assertFalse($tdispatcher->hasListeners('foo')); + + $tdispatcher->addListener('foo', $listener = function () {}); + $this->assertTrue($dispatcher->hasListeners('foo')); + $this->assertTrue($tdispatcher->hasListeners('foo')); + } + + public function testGetListenerPriority() + { + $dispatcher = new EventDispatcher(); + $tdispatcher = new TraceableEventDispatcher($dispatcher, new Stopwatch()); + + $tdispatcher->addListener('foo', function () {}, 123); + + $listeners = $dispatcher->getListeners('foo'); + $this->assertSame(123, $tdispatcher->getListenerPriority('foo', $listeners[0])); + + // Verify that priority is preserved when listener is removed and re-added + // in preProcess() and postProcess(). + $tdispatcher->dispatch('foo', new Event()); + $listeners = $dispatcher->getListeners('foo'); + $this->assertSame(123, $tdispatcher->getListenerPriority('foo', $listeners[0])); + } + + public function testGetListenerPriorityWhileDispatching() + { + $tdispatcher = new TraceableEventDispatcher(new EventDispatcher(), new Stopwatch()); + $priorityWhileDispatching = null; + + $listener = function () use ($tdispatcher, &$priorityWhileDispatching, &$listener) { + $priorityWhileDispatching = $tdispatcher->getListenerPriority('bar', $listener); + }; + + $tdispatcher->addListener('bar', $listener, 5); + $tdispatcher->dispatch('bar'); + $this->assertSame(5, $priorityWhileDispatching); + } + + public function testAddRemoveSubscriber() + { + $dispatcher = new EventDispatcher(); + $tdispatcher = new TraceableEventDispatcher($dispatcher, new Stopwatch()); + + $subscriber = new EventSubscriber(); + + $tdispatcher->addSubscriber($subscriber); + $listeners = $dispatcher->getListeners('foo'); + $this->assertCount(1, $listeners); + $this->assertSame(array($subscriber, 'call'), $listeners[0]); + + $tdispatcher->removeSubscriber($subscriber); + $this->assertCount(0, $dispatcher->getListeners('foo')); + } + + public function testGetCalledListeners() + { + $tdispatcher = new TraceableEventDispatcher(new EventDispatcher(), new Stopwatch()); + $tdispatcher->addListener('foo', function () {}, 5); + + $listeners = $tdispatcher->getNotCalledListeners(); + $this->assertArrayHasKey('stub', $listeners['foo.closure']); + unset($listeners['foo.closure']['stub']); + $this->assertEquals(array(), $tdispatcher->getCalledListeners()); + $this->assertEquals(array('foo.closure' => array('event' => 'foo', 'pretty' => 'closure', 'priority' => 5)), $listeners); + + $tdispatcher->dispatch('foo'); + + $listeners = $tdispatcher->getCalledListeners(); + $this->assertArrayHasKey('stub', $listeners['foo.closure']); + unset($listeners['foo.closure']['stub']); + $this->assertEquals(array('foo.closure' => array('event' => 'foo', 'pretty' => 'closure', 'priority' => 5)), $listeners); + $this->assertEquals(array(), $tdispatcher->getNotCalledListeners()); + } + + public function testClearCalledListeners() + { + $tdispatcher = new TraceableEventDispatcher(new EventDispatcher(), new Stopwatch()); + $tdispatcher->addListener('foo', function () {}, 5); + + $tdispatcher->dispatch('foo'); + $tdispatcher->reset(); + + $listeners = $tdispatcher->getNotCalledListeners(); + $this->assertArrayHasKey('stub', $listeners['foo.closure']); + unset($listeners['foo.closure']['stub']); + $this->assertEquals(array(), $tdispatcher->getCalledListeners()); + $this->assertEquals(array('foo.closure' => array('event' => 'foo', 'pretty' => 'closure', 'priority' => 5)), $listeners); + } + + public function testGetCalledListenersNested() + { + $tdispatcher = null; + $dispatcher = new TraceableEventDispatcher(new EventDispatcher(), new Stopwatch()); + $dispatcher->addListener('foo', function (Event $event, $eventName, $dispatcher) use (&$tdispatcher) { + $tdispatcher = $dispatcher; + $dispatcher->dispatch('bar'); + }); + $dispatcher->addListener('bar', function (Event $event) {}); + $dispatcher->dispatch('foo'); + $this->assertSame($dispatcher, $tdispatcher); + $this->assertCount(2, $dispatcher->getCalledListeners()); + } + + public function testLogger() + { + $logger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock(); + + $dispatcher = new EventDispatcher(); + $tdispatcher = new TraceableEventDispatcher($dispatcher, new Stopwatch(), $logger); + $tdispatcher->addListener('foo', $listener1 = function () {}); + $tdispatcher->addListener('foo', $listener2 = function () {}); + + $logger->expects($this->at(0))->method('debug')->with('Notified event "{event}" to listener "{listener}".', array('event' => 'foo', 'listener' => 'closure')); + $logger->expects($this->at(1))->method('debug')->with('Notified event "{event}" to listener "{listener}".', array('event' => 'foo', 'listener' => 'closure')); + + $tdispatcher->dispatch('foo'); + } + + public function testLoggerWithStoppedEvent() + { + $logger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock(); + + $dispatcher = new EventDispatcher(); + $tdispatcher = new TraceableEventDispatcher($dispatcher, new Stopwatch(), $logger); + $tdispatcher->addListener('foo', $listener1 = function (Event $event) { $event->stopPropagation(); }); + $tdispatcher->addListener('foo', $listener2 = function () {}); + + $logger->expects($this->at(0))->method('debug')->with('Notified event "{event}" to listener "{listener}".', array('event' => 'foo', 'listener' => 'closure')); + $logger->expects($this->at(1))->method('debug')->with('Listener "{listener}" stopped propagation of the event "{event}".', array('event' => 'foo', 'listener' => 'closure')); + $logger->expects($this->at(2))->method('debug')->with('Listener "{listener}" was not called for event "{event}".', array('event' => 'foo', 'listener' => 'closure')); + + $tdispatcher->dispatch('foo'); + } + + public function testDispatchCallListeners() + { + $called = array(); + + $dispatcher = new EventDispatcher(); + $tdispatcher = new TraceableEventDispatcher($dispatcher, new Stopwatch()); + $tdispatcher->addListener('foo', function () use (&$called) { $called[] = 'foo1'; }, 10); + $tdispatcher->addListener('foo', function () use (&$called) { $called[] = 'foo2'; }, 20); + + $tdispatcher->dispatch('foo'); + + $this->assertSame(array('foo2', 'foo1'), $called); + } + + public function testDispatchNested() + { + $dispatcher = new TraceableEventDispatcher(new EventDispatcher(), new Stopwatch()); + $loop = 1; + $dispatchedEvents = 0; + $dispatcher->addListener('foo', $listener1 = function () use ($dispatcher, &$loop) { + ++$loop; + if (2 == $loop) { + $dispatcher->dispatch('foo'); + } + }); + $dispatcher->addListener('foo', function () use (&$dispatchedEvents) { + ++$dispatchedEvents; + }); + + $dispatcher->dispatch('foo'); + + $this->assertSame(2, $dispatchedEvents); + } + + public function testDispatchReusedEventNested() + { + $nestedCall = false; + $dispatcher = new TraceableEventDispatcher(new EventDispatcher(), new Stopwatch()); + $dispatcher->addListener('foo', function (Event $e) use ($dispatcher) { + $dispatcher->dispatch('bar', $e); + }); + $dispatcher->addListener('bar', function (Event $e) use (&$nestedCall) { + $nestedCall = true; + }); + + $this->assertFalse($nestedCall); + $dispatcher->dispatch('foo'); + $this->assertTrue($nestedCall); + } + + public function testListenerCanRemoveItselfWhenExecuted() + { + $eventDispatcher = new TraceableEventDispatcher(new EventDispatcher(), new Stopwatch()); + $listener1 = function ($event, $eventName, EventDispatcherInterface $dispatcher) use (&$listener1) { + $dispatcher->removeListener('foo', $listener1); + }; + $eventDispatcher->addListener('foo', $listener1); + $eventDispatcher->addListener('foo', function () {}); + $eventDispatcher->dispatch('foo'); + + $this->assertCount(1, $eventDispatcher->getListeners('foo'), 'expected listener1 to be removed'); + } +} + +class EventSubscriber implements EventSubscriberInterface +{ + public static function getSubscribedEvents() + { + return array('foo' => 'call'); + } +} diff --git a/lib/silex/vendor/symfony/event-dispatcher/Tests/DependencyInjection/RegisterListenersPassTest.php b/lib/silex/vendor/symfony/event-dispatcher/Tests/DependencyInjection/RegisterListenersPassTest.php new file mode 100644 index 000000000..dbb1aa5c5 --- /dev/null +++ b/lib/silex/vendor/symfony/event-dispatcher/Tests/DependencyInjection/RegisterListenersPassTest.php @@ -0,0 +1,179 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\EventDispatcher\Tests\DependencyInjection; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\EventDispatcher\DependencyInjection\RegisterListenersPass; + +class RegisterListenersPassTest extends TestCase +{ + /** + * Tests that event subscribers not implementing EventSubscriberInterface + * trigger an exception. + * + * @expectedException \InvalidArgumentException + */ + public function testEventSubscriberWithoutInterface() + { + // one service, not implementing any interface + $services = array( + 'my_event_subscriber' => array(0 => array()), + ); + + $definition = $this->getMockBuilder('Symfony\Component\DependencyInjection\Definition')->getMock(); + $definition->expects($this->atLeastOnce()) + ->method('getClass') + ->will($this->returnValue('stdClass')); + + $builder = $this->getMockBuilder('Symfony\Component\DependencyInjection\ContainerBuilder')->setMethods(array('hasDefinition', 'findTaggedServiceIds', 'getDefinition'))->getMock(); + $builder->expects($this->any()) + ->method('hasDefinition') + ->will($this->returnValue(true)); + + // We don't test kernel.event_listener here + $builder->expects($this->atLeastOnce()) + ->method('findTaggedServiceIds') + ->will($this->onConsecutiveCalls(array(), $services)); + + $builder->expects($this->atLeastOnce()) + ->method('getDefinition') + ->will($this->returnValue($definition)); + + $registerListenersPass = new RegisterListenersPass(); + $registerListenersPass->process($builder); + } + + public function testValidEventSubscriber() + { + $services = array( + 'my_event_subscriber' => array(0 => array()), + ); + + $definition = $this->getMockBuilder('Symfony\Component\DependencyInjection\Definition')->getMock(); + $definition->expects($this->atLeastOnce()) + ->method('getClass') + ->will($this->returnValue('Symfony\Component\EventDispatcher\Tests\DependencyInjection\SubscriberService')); + + $builder = $this->getMockBuilder('Symfony\Component\DependencyInjection\ContainerBuilder')->setMethods(array('hasDefinition', 'findTaggedServiceIds', 'getDefinition', 'findDefinition'))->getMock(); + $builder->expects($this->any()) + ->method('hasDefinition') + ->will($this->returnValue(true)); + + // We don't test kernel.event_listener here + $builder->expects($this->atLeastOnce()) + ->method('findTaggedServiceIds') + ->will($this->onConsecutiveCalls(array(), $services)); + + $builder->expects($this->atLeastOnce()) + ->method('getDefinition') + ->will($this->returnValue($definition)); + + $builder->expects($this->atLeastOnce()) + ->method('findDefinition') + ->will($this->returnValue($definition)); + + $registerListenersPass = new RegisterListenersPass(); + $registerListenersPass->process($builder); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage The service "foo" tagged "kernel.event_listener" must not be abstract. + */ + public function testAbstractEventListener() + { + $container = new ContainerBuilder(); + $container->register('foo', 'stdClass')->setAbstract(true)->addTag('kernel.event_listener', array()); + $container->register('event_dispatcher', 'stdClass'); + + $registerListenersPass = new RegisterListenersPass(); + $registerListenersPass->process($container); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage The service "foo" tagged "kernel.event_subscriber" must not be abstract. + */ + public function testAbstractEventSubscriber() + { + $container = new ContainerBuilder(); + $container->register('foo', 'stdClass')->setAbstract(true)->addTag('kernel.event_subscriber', array()); + $container->register('event_dispatcher', 'stdClass'); + + $registerListenersPass = new RegisterListenersPass(); + $registerListenersPass->process($container); + } + + public function testEventSubscriberResolvableClassName() + { + $container = new ContainerBuilder(); + + $container->setParameter('subscriber.class', 'Symfony\Component\EventDispatcher\Tests\DependencyInjection\SubscriberService'); + $container->register('foo', '%subscriber.class%')->addTag('kernel.event_subscriber', array()); + $container->register('event_dispatcher', 'stdClass'); + + $registerListenersPass = new RegisterListenersPass(); + $registerListenersPass->process($container); + + $definition = $container->getDefinition('event_dispatcher'); + $expectedCalls = array( + array( + 'addListener', + array( + 'event', + array(new ServiceClosureArgument(new Reference('foo')), 'onEvent'), + 0, + ), + ), + ); + $this->assertEquals($expectedCalls, $definition->getMethodCalls()); + } + + public function testHotPathEvents() + { + $container = new ContainerBuilder(); + + $container->register('foo', SubscriberService::class)->addTag('kernel.event_subscriber', array()); + $container->register('event_dispatcher', 'stdClass'); + + (new RegisterListenersPass())->setHotPathEvents(array('event'))->process($container); + + $this->assertTrue($container->getDefinition('foo')->hasTag('container.hot_path')); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage You have requested a non-existent parameter "subscriber.class" + */ + public function testEventSubscriberUnresolvableClassName() + { + $container = new ContainerBuilder(); + $container->register('foo', '%subscriber.class%')->addTag('kernel.event_subscriber', array()); + $container->register('event_dispatcher', 'stdClass'); + + $registerListenersPass = new RegisterListenersPass(); + $registerListenersPass->process($container); + } +} + +class SubscriberService implements \Symfony\Component\EventDispatcher\EventSubscriberInterface +{ + public static function getSubscribedEvents() + { + return array( + 'event' => 'onEvent', + ); + } +} diff --git a/lib/silex/vendor/symfony/event-dispatcher/Tests/EventDispatcherTest.php b/lib/silex/vendor/symfony/event-dispatcher/Tests/EventDispatcherTest.php new file mode 100644 index 000000000..5faa5c8be --- /dev/null +++ b/lib/silex/vendor/symfony/event-dispatcher/Tests/EventDispatcherTest.php @@ -0,0 +1,22 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\EventDispatcher\Tests; + +use Symfony\Component\EventDispatcher\EventDispatcher; + +class EventDispatcherTest extends AbstractEventDispatcherTest +{ + protected function createEventDispatcher() + { + return new EventDispatcher(); + } +} diff --git a/lib/silex/vendor/symfony/event-dispatcher/Tests/EventTest.php b/lib/silex/vendor/symfony/event-dispatcher/Tests/EventTest.php new file mode 100644 index 000000000..5be2ea09f --- /dev/null +++ b/lib/silex/vendor/symfony/event-dispatcher/Tests/EventTest.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\EventDispatcher\Tests; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\EventDispatcher\Event; + +/** + * Test class for Event. + */ +class EventTest extends TestCase +{ + /** + * @var \Symfony\Component\EventDispatcher\Event + */ + protected $event; + + /** + * Sets up the fixture, for example, opens a network connection. + * This method is called before a test is executed. + */ + protected function setUp() + { + $this->event = new Event(); + } + + /** + * Tears down the fixture, for example, closes a network connection. + * This method is called after a test is executed. + */ + protected function tearDown() + { + $this->event = null; + } + + public function testIsPropagationStopped() + { + $this->assertFalse($this->event->isPropagationStopped()); + } + + public function testStopPropagationAndIsPropagationStopped() + { + $this->event->stopPropagation(); + $this->assertTrue($this->event->isPropagationStopped()); + } +} diff --git a/lib/silex/vendor/symfony/event-dispatcher/Tests/GenericEventTest.php b/lib/silex/vendor/symfony/event-dispatcher/Tests/GenericEventTest.php new file mode 100644 index 000000000..9cf68c987 --- /dev/null +++ b/lib/silex/vendor/symfony/event-dispatcher/Tests/GenericEventTest.php @@ -0,0 +1,140 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\EventDispatcher\Tests; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\EventDispatcher\GenericEvent; + +/** + * Test class for Event. + */ +class GenericEventTest extends TestCase +{ + /** + * @var GenericEvent + */ + private $event; + + private $subject; + + /** + * Prepares the environment before running a test. + */ + protected function setUp() + { + parent::setUp(); + + $this->subject = new \stdClass(); + $this->event = new GenericEvent($this->subject, array('name' => 'Event')); + } + + /** + * Cleans up the environment after running a test. + */ + protected function tearDown() + { + $this->subject = null; + $this->event = null; + + parent::tearDown(); + } + + public function testConstruct() + { + $this->assertEquals($this->event, new GenericEvent($this->subject, array('name' => 'Event'))); + } + + /** + * Tests Event->getArgs(). + */ + public function testGetArguments() + { + // test getting all + $this->assertSame(array('name' => 'Event'), $this->event->getArguments()); + } + + public function testSetArguments() + { + $result = $this->event->setArguments(array('foo' => 'bar')); + $this->assertAttributeSame(array('foo' => 'bar'), 'arguments', $this->event); + $this->assertSame($this->event, $result); + } + + public function testSetArgument() + { + $result = $this->event->setArgument('foo2', 'bar2'); + $this->assertAttributeSame(array('name' => 'Event', 'foo2' => 'bar2'), 'arguments', $this->event); + $this->assertEquals($this->event, $result); + } + + public function testGetArgument() + { + // test getting key + $this->assertEquals('Event', $this->event->getArgument('name')); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testGetArgException() + { + $this->event->getArgument('nameNotExist'); + } + + public function testOffsetGet() + { + // test getting key + $this->assertEquals('Event', $this->event['name']); + + // test getting invalid arg + $this->{method_exists($this, $_ = 'expectException') ? $_ : 'setExpectedException'}('InvalidArgumentException'); + $this->assertFalse($this->event['nameNotExist']); + } + + public function testOffsetSet() + { + $this->event['foo2'] = 'bar2'; + $this->assertAttributeSame(array('name' => 'Event', 'foo2' => 'bar2'), 'arguments', $this->event); + } + + public function testOffsetUnset() + { + unset($this->event['name']); + $this->assertAttributeSame(array(), 'arguments', $this->event); + } + + public function testOffsetIsset() + { + $this->assertArrayHasKey('name', $this->event); + $this->assertArrayNotHasKey('nameNotExist', $this->event); + } + + public function testHasArgument() + { + $this->assertTrue($this->event->hasArgument('name')); + $this->assertFalse($this->event->hasArgument('nameNotExist')); + } + + public function testGetSubject() + { + $this->assertSame($this->subject, $this->event->getSubject()); + } + + public function testHasIterator() + { + $data = array(); + foreach ($this->event as $key => $value) { + $data[$key] = $value; + } + $this->assertEquals(array('name' => 'Event'), $data); + } +} diff --git a/lib/silex/vendor/symfony/event-dispatcher/Tests/ImmutableEventDispatcherTest.php b/lib/silex/vendor/symfony/event-dispatcher/Tests/ImmutableEventDispatcherTest.php new file mode 100644 index 000000000..04f2861e3 --- /dev/null +++ b/lib/silex/vendor/symfony/event-dispatcher/Tests/ImmutableEventDispatcherTest.php @@ -0,0 +1,106 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\EventDispatcher\Tests; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\EventDispatcher\Event; +use Symfony\Component\EventDispatcher\ImmutableEventDispatcher; + +/** + * @author Bernhard Schussek + */ +class ImmutableEventDispatcherTest extends TestCase +{ + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + private $innerDispatcher; + + /** + * @var ImmutableEventDispatcher + */ + private $dispatcher; + + protected function setUp() + { + $this->innerDispatcher = $this->getMockBuilder('Symfony\Component\EventDispatcher\EventDispatcherInterface')->getMock(); + $this->dispatcher = new ImmutableEventDispatcher($this->innerDispatcher); + } + + public function testDispatchDelegates() + { + $event = new Event(); + + $this->innerDispatcher->expects($this->once()) + ->method('dispatch') + ->with('event', $event) + ->will($this->returnValue('result')); + + $this->assertSame('result', $this->dispatcher->dispatch('event', $event)); + } + + public function testGetListenersDelegates() + { + $this->innerDispatcher->expects($this->once()) + ->method('getListeners') + ->with('event') + ->will($this->returnValue('result')); + + $this->assertSame('result', $this->dispatcher->getListeners('event')); + } + + public function testHasListenersDelegates() + { + $this->innerDispatcher->expects($this->once()) + ->method('hasListeners') + ->with('event') + ->will($this->returnValue('result')); + + $this->assertSame('result', $this->dispatcher->hasListeners('event')); + } + + /** + * @expectedException \BadMethodCallException + */ + public function testAddListenerDisallowed() + { + $this->dispatcher->addListener('event', function () { return 'foo'; }); + } + + /** + * @expectedException \BadMethodCallException + */ + public function testAddSubscriberDisallowed() + { + $subscriber = $this->getMockBuilder('Symfony\Component\EventDispatcher\EventSubscriberInterface')->getMock(); + + $this->dispatcher->addSubscriber($subscriber); + } + + /** + * @expectedException \BadMethodCallException + */ + public function testRemoveListenerDisallowed() + { + $this->dispatcher->removeListener('event', function () { return 'foo'; }); + } + + /** + * @expectedException \BadMethodCallException + */ + public function testRemoveSubscriberDisallowed() + { + $subscriber = $this->getMockBuilder('Symfony\Component\EventDispatcher\EventSubscriberInterface')->getMock(); + + $this->dispatcher->removeSubscriber($subscriber); + } +} diff --git a/lib/silex/vendor/symfony/event-dispatcher/composer.json b/lib/silex/vendor/symfony/event-dispatcher/composer.json new file mode 100644 index 000000000..75b881b91 --- /dev/null +++ b/lib/silex/vendor/symfony/event-dispatcher/composer.json @@ -0,0 +1,47 @@ +{ + "name": "symfony/event-dispatcher", + "type": "library", + "description": "Symfony EventDispatcher Component", + "keywords": [], + "homepage": "https://symfony.com", + "license": "MIT", + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "require": { + "php": "^5.5.9|>=7.0.8" + }, + "require-dev": { + "symfony/dependency-injection": "~3.3|~4.0", + "symfony/expression-language": "~2.8|~3.0|~4.0", + "symfony/config": "~2.8|~3.0|~4.0", + "symfony/stopwatch": "~2.8|~3.0|~4.0", + "psr/log": "~1.0" + }, + "conflict": { + "symfony/dependency-injection": "<3.3" + }, + "suggest": { + "symfony/dependency-injection": "", + "symfony/http-kernel": "" + }, + "autoload": { + "psr-4": { "Symfony\\Component\\EventDispatcher\\": "" }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "minimum-stability": "dev", + "extra": { + "branch-alias": { + "dev-master": "3.4-dev" + } + } +} diff --git a/lib/silex/vendor/symfony/event-dispatcher/phpunit.xml.dist b/lib/silex/vendor/symfony/event-dispatcher/phpunit.xml.dist new file mode 100644 index 000000000..b3ad1bdf5 --- /dev/null +++ b/lib/silex/vendor/symfony/event-dispatcher/phpunit.xml.dist @@ -0,0 +1,31 @@ + + + + + + + + + + ./Tests/ + + + + + + ./ + + ./Resources + ./Tests + ./vendor + + + + diff --git a/lib/silex/vendor/symfony/http-foundation/.gitignore b/lib/silex/vendor/symfony/http-foundation/.gitignore new file mode 100644 index 000000000..c49a5d8df --- /dev/null +++ b/lib/silex/vendor/symfony/http-foundation/.gitignore @@ -0,0 +1,3 @@ +vendor/ +composer.lock +phpunit.xml diff --git a/lib/silex/vendor/symfony/http-foundation/AcceptHeader.php b/lib/silex/vendor/symfony/http-foundation/AcceptHeader.php new file mode 100644 index 000000000..d1740266b --- /dev/null +++ b/lib/silex/vendor/symfony/http-foundation/AcceptHeader.php @@ -0,0 +1,168 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation; + +/** + * Represents an Accept-* header. + * + * An accept header is compound with a list of items, + * sorted by descending quality. + * + * @author Jean-François Simon + */ +class AcceptHeader +{ + /** + * @var AcceptHeaderItem[] + */ + private $items = array(); + + /** + * @var bool + */ + private $sorted = true; + + /** + * @param AcceptHeaderItem[] $items + */ + public function __construct(array $items) + { + foreach ($items as $item) { + $this->add($item); + } + } + + /** + * Builds an AcceptHeader instance from a string. + * + * @param string $headerValue + * + * @return self + */ + public static function fromString($headerValue) + { + $index = 0; + + return new self(array_map(function ($itemValue) use (&$index) { + $item = AcceptHeaderItem::fromString($itemValue); + $item->setIndex($index++); + + return $item; + }, preg_split('/\s*(?:,*("[^"]+"),*|,*(\'[^\']+\'),*|,+)\s*/', $headerValue, 0, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE))); + } + + /** + * Returns header value's string representation. + * + * @return string + */ + public function __toString() + { + return implode(',', $this->items); + } + + /** + * Tests if header has given value. + * + * @param string $value + * + * @return bool + */ + public function has($value) + { + return isset($this->items[$value]); + } + + /** + * Returns given value's item, if exists. + * + * @param string $value + * + * @return AcceptHeaderItem|null + */ + public function get($value) + { + return isset($this->items[$value]) ? $this->items[$value] : null; + } + + /** + * Adds an item. + * + * @return $this + */ + public function add(AcceptHeaderItem $item) + { + $this->items[$item->getValue()] = $item; + $this->sorted = false; + + return $this; + } + + /** + * Returns all items. + * + * @return AcceptHeaderItem[] + */ + public function all() + { + $this->sort(); + + return $this->items; + } + + /** + * Filters items on their value using given regex. + * + * @param string $pattern + * + * @return self + */ + public function filter($pattern) + { + return new self(array_filter($this->items, function (AcceptHeaderItem $item) use ($pattern) { + return preg_match($pattern, $item->getValue()); + })); + } + + /** + * Returns first item. + * + * @return AcceptHeaderItem|null + */ + public function first() + { + $this->sort(); + + return !empty($this->items) ? reset($this->items) : null; + } + + /** + * Sorts items by descending quality. + */ + private function sort() + { + if (!$this->sorted) { + uasort($this->items, function (AcceptHeaderItem $a, AcceptHeaderItem $b) { + $qA = $a->getQuality(); + $qB = $b->getQuality(); + + if ($qA === $qB) { + return $a->getIndex() > $b->getIndex() ? 1 : -1; + } + + return $qA > $qB ? -1 : 1; + }); + + $this->sorted = true; + } + } +} diff --git a/lib/silex/vendor/symfony/http-foundation/AcceptHeaderItem.php b/lib/silex/vendor/symfony/http-foundation/AcceptHeaderItem.php new file mode 100644 index 000000000..c69dbbba3 --- /dev/null +++ b/lib/silex/vendor/symfony/http-foundation/AcceptHeaderItem.php @@ -0,0 +1,209 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation; + +/** + * Represents an Accept-* header item. + * + * @author Jean-François Simon + */ +class AcceptHeaderItem +{ + private $value; + private $quality = 1.0; + private $index = 0; + private $attributes = array(); + + /** + * @param string $value + * @param array $attributes + */ + public function __construct($value, array $attributes = array()) + { + $this->value = $value; + foreach ($attributes as $name => $value) { + $this->setAttribute($name, $value); + } + } + + /** + * Builds an AcceptHeaderInstance instance from a string. + * + * @param string $itemValue + * + * @return self + */ + 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] : ''; + } + } + + return new self(($start = substr($value, 0, 1)) === ($end = substr($value, -1)) && ('"' === $start || '\'' === $start) ? substr($value, 1, -1) : $value, $attributes); + } + + /** + * Returns header value's string representation. + * + * @return string + */ + 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)); + } + + return $string; + } + + /** + * Set the item value. + * + * @param string $value + * + * @return $this + */ + public function setValue($value) + { + $this->value = $value; + + return $this; + } + + /** + * Returns the item value. + * + * @return string + */ + public function getValue() + { + return $this->value; + } + + /** + * Set the item quality. + * + * @param float $quality + * + * @return $this + */ + public function setQuality($quality) + { + $this->quality = $quality; + + return $this; + } + + /** + * Returns the item quality. + * + * @return float + */ + public function getQuality() + { + return $this->quality; + } + + /** + * Set the item index. + * + * @param int $index + * + * @return $this + */ + public function setIndex($index) + { + $this->index = $index; + + return $this; + } + + /** + * Returns the item index. + * + * @return int + */ + public function getIndex() + { + return $this->index; + } + + /** + * Tests if an attribute exists. + * + * @param string $name + * + * @return bool + */ + public function hasAttribute($name) + { + return isset($this->attributes[$name]); + } + + /** + * Returns an attribute by its name. + * + * @param string $name + * @param mixed $default + * + * @return mixed + */ + public function getAttribute($name, $default = null) + { + return isset($this->attributes[$name]) ? $this->attributes[$name] : $default; + } + + /** + * Returns all attributes. + * + * @return array + */ + public function getAttributes() + { + return $this->attributes; + } + + /** + * Set an attribute. + * + * @param string $name + * @param string $value + * + * @return $this + */ + public function setAttribute($name, $value) + { + if ('q' === $name) { + $this->quality = (float) $value; + } else { + $this->attributes[$name] = (string) $value; + } + + return $this; + } +} diff --git a/lib/silex/vendor/symfony/http-foundation/ApacheRequest.php b/lib/silex/vendor/symfony/http-foundation/ApacheRequest.php new file mode 100644 index 000000000..84803ebae --- /dev/null +++ b/lib/silex/vendor/symfony/http-foundation/ApacheRequest.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\HttpFoundation; + +/** + * Request represents an HTTP request from an Apache server. + * + * @author Fabien Potencier + */ +class ApacheRequest extends Request +{ + /** + * {@inheritdoc} + */ + protected function prepareRequestUri() + { + return $this->server->get('REQUEST_URI'); + } + + /** + * {@inheritdoc} + */ + protected function prepareBaseUrl() + { + $baseUrl = $this->server->get('SCRIPT_NAME'); + + if (false === strpos($this->server->get('REQUEST_URI'), $baseUrl)) { + // assume mod_rewrite + return rtrim(dirname($baseUrl), '/\\'); + } + + return $baseUrl; + } +} diff --git a/lib/silex/vendor/symfony/http-foundation/BinaryFileResponse.php b/lib/silex/vendor/symfony/http-foundation/BinaryFileResponse.php new file mode 100644 index 000000000..101022304 --- /dev/null +++ b/lib/silex/vendor/symfony/http-foundation/BinaryFileResponse.php @@ -0,0 +1,359 @@ + + * + * 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\HttpFoundation\File\File; +use Symfony\Component\HttpFoundation\File\Exception\FileException; + +/** + * BinaryFileResponse represents an HTTP response delivering a file. + * + * @author Niklas Fiekas + * @author stealth35 + * @author Igor Wiedler + * @author Jordan Alliot + * @author Sergey Linnik + */ +class BinaryFileResponse extends Response +{ + protected static $trustXSendfileTypeHeader = false; + + /** + * @var File + */ + protected $file; + protected $offset; + protected $maxlen; + protected $deleteFileAfterSend = false; + + /** + * @param \SplFileInfo|string $file The file to stream + * @param int $status The response status code + * @param array $headers An array of response headers + * @param bool $public Files are public by default + * @param null|string $contentDisposition The type of Content-Disposition to set automatically with the filename + * @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) + { + parent::__construct(null, $status, $headers); + + $this->setFile($file, $contentDisposition, $autoEtag, $autoLastModified); + + if ($public) { + $this->setPublic(); + } + } + + /** + * @param \SplFileInfo|string $file The file to stream + * @param int $status The response status code + * @param array $headers An array of response headers + * @param bool $public Files are public by default + * @param null|string $contentDisposition The type of Content-Disposition to set automatically with the filename + * @param bool $autoEtag Whether the ETag header should be automatically set + * @param bool $autoLastModified Whether the Last-Modified header should be automatically set + * + * @return static + */ + public static function create($file = null, $status = 200, $headers = array(), $public = true, $contentDisposition = null, $autoEtag = false, $autoLastModified = true) + { + return new static($file, $status, $headers, $public, $contentDisposition, $autoEtag, $autoLastModified); + } + + /** + * Sets the file to stream. + * + * @param \SplFileInfo|string $file The file to stream + * @param string $contentDisposition + * @param bool $autoEtag + * @param bool $autoLastModified + * + * @return $this + * + * @throws FileException + */ + public function setFile($file, $contentDisposition = null, $autoEtag = false, $autoLastModified = true) + { + if (!$file instanceof File) { + if ($file instanceof \SplFileInfo) { + $file = new File($file->getPathname()); + } else { + $file = new File((string) $file); + } + } + + if (!$file->isReadable()) { + throw new FileException('File must be readable.'); + } + + $this->file = $file; + + if ($autoEtag) { + $this->setAutoEtag(); + } + + if ($autoLastModified) { + $this->setAutoLastModified(); + } + + if ($contentDisposition) { + $this->setContentDisposition($contentDisposition); + } + + return $this; + } + + /** + * Gets the file. + * + * @return File The file to stream + */ + public function getFile() + { + return $this->file; + } + + /** + * Automatically sets the Last-Modified header according the file modification date. + */ + public function setAutoLastModified() + { + $this->setLastModified(\DateTime::createFromFormat('U', $this->file->getMTime())); + + return $this; + } + + /** + * Automatically sets the ETag header according to the checksum of the file. + */ + public function setAutoEtag() + { + $this->setEtag(base64_encode(hash_file('sha256', $this->file->getPathname(), true))); + + return $this; + } + + /** + * Sets the Content-Disposition header with the given filename. + * + * @param string $disposition ResponseHeaderBag::DISPOSITION_INLINE or ResponseHeaderBag::DISPOSITION_ATTACHMENT + * @param string $filename Optionally use this UTF-8 encoded filename instead of the real name of the file + * @param string $filenameFallback A fallback filename, containing only ASCII characters. Defaults to an automatically encoded filename + * + * @return $this + */ + public function setContentDisposition($disposition, $filename = '', $filenameFallback = '') + { + if ('' === $filename) { + $filename = $this->file->getFilename(); + } + + if ('' === $filenameFallback && (!preg_match('/^[\x20-\x7e]*$/', $filename) || false !== strpos($filename, '%'))) { + $encoding = mb_detect_encoding($filename, null, true) ?: '8bit'; + + for ($i = 0, $filenameLength = mb_strlen($filename, $encoding); $i < $filenameLength; ++$i) { + $char = mb_substr($filename, $i, 1, $encoding); + + if ('%' === $char || ord($char) < 32 || ord($char) > 126) { + $filenameFallback .= '_'; + } else { + $filenameFallback .= $char; + } + } + } + + $dispositionHeader = $this->headers->makeDisposition($disposition, $filename, $filenameFallback); + $this->headers->set('Content-Disposition', $dispositionHeader); + + return $this; + } + + /** + * {@inheritdoc} + */ + public function prepare(Request $request) + { + if (!$this->headers->has('Content-Type')) { + $this->headers->set('Content-Type', $this->file->getMimeType() ?: 'application/octet-stream'); + } + + if ('HTTP/1.0' !== $request->server->get('SERVER_PROTOCOL')) { + $this->setProtocolVersion('1.1'); + } + + $this->ensureIEOverSSLCompatibility($request); + + $this->offset = 0; + $this->maxlen = -1; + + if (false === $fileSize = $this->file->getSize()) { + return $this; + } + $this->headers->set('Content-Length', $fileSize); + + if (!$this->headers->has('Accept-Ranges')) { + // Only accept ranges on safe HTTP methods + $this->headers->set('Accept-Ranges', $request->isMethodSafe(false) ? 'bytes' : 'none'); + } + + if (self::$trustXSendfileTypeHeader && $request->headers->has('X-Sendfile-Type')) { + // Use X-Sendfile, do not send any content. + $type = $request->headers->get('X-Sendfile-Type'); + $path = $this->file->getRealPath(); + // Fall back to scheme://path for stream wrapped locations. + if (false === $path) { + $path = $this->file->getPathname(); + } + 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; + } + } + } + } + $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); + + $end = ('' === $end) ? $fileSize - 1 : (int) $end; + + if ('' === $start) { + $start = $fileSize - $end; + $end = $fileSize - 1; + } else { + $start = (int) $start; + } + + if ($start <= $end) { + if ($start < 0 || $end > $fileSize - 1) { + $this->setStatusCode(416); + $this->headers->set('Content-Range', sprintf('bytes */%s', $fileSize)); + } elseif (0 !== $start || $end !== $fileSize - 1) { + $this->maxlen = $end < $fileSize ? $end - $start + 1 : -1; + $this->offset = $start; + + $this->setStatusCode(206); + $this->headers->set('Content-Range', sprintf('bytes %s-%s/%s', $start, $end, $fileSize)); + $this->headers->set('Content-Length', $end - $start + 1); + } + } + } + } + + return $this; + } + + private function hasValidIfRangeHeader($header) + { + if ($this->getEtag() === $header) { + return true; + } + + if (null === $lastModified = $this->getLastModified()) { + return false; + } + + return $lastModified->format('D, d M Y H:i:s').' GMT' === $header; + } + + /** + * Sends the file. + * + * {@inheritdoc} + */ + public function sendContent() + { + if (!$this->isSuccessful()) { + return parent::sendContent(); + } + + if (0 === $this->maxlen) { + return $this; + } + + $out = fopen('php://output', 'wb'); + $file = fopen($this->file->getPathname(), 'rb'); + + stream_copy_to_stream($file, $out, $this->maxlen, $this->offset); + + fclose($out); + fclose($file); + + if ($this->deleteFileAfterSend) { + unlink($this->file->getPathname()); + } + + return $this; + } + + /** + * {@inheritdoc} + * + * @throws \LogicException when the content is not null + */ + public function setContent($content) + { + if (null !== $content) { + throw new \LogicException('The content cannot be set on a BinaryFileResponse instance.'); + } + } + + /** + * {@inheritdoc} + * + * @return false + */ + public function getContent() + { + return false; + } + + /** + * Trust X-Sendfile-Type header. + */ + public static function trustXSendfileTypeHeader() + { + self::$trustXSendfileTypeHeader = true; + } + + /** + * If this is set to true, the file will be unlinked after the request is send + * Note: If the X-Sendfile header is used, the deleteFileAfterSend setting will not be used. + * + * @param bool $shouldDelete + * + * @return $this + */ + public function deleteFileAfterSend($shouldDelete) + { + $this->deleteFileAfterSend = $shouldDelete; + + return $this; + } +} diff --git a/lib/silex/vendor/symfony/http-foundation/CHANGELOG.md b/lib/silex/vendor/symfony/http-foundation/CHANGELOG.md new file mode 100644 index 000000000..ee5b6cecf --- /dev/null +++ b/lib/silex/vendor/symfony/http-foundation/CHANGELOG.md @@ -0,0 +1,159 @@ +CHANGELOG +========= + +3.4.0 +----- + + * implemented PHP 7.0's `SessionUpdateTimestampHandlerInterface` with a new + `AbstractSessionHandler` base class and a new `StrictSessionHandler` wrapper + * deprecated the `WriteCheckSessionHandler`, `NativeSessionHandler` and `NativeProxy` classes + * deprecated setting session save handlers that do not implement `\SessionHandlerInterface` in `NativeSessionStorage::setSaveHandler()` + * deprecated using `MongoDbSessionHandler` with the legacy mongo extension; use it with the mongodb/mongodb package and ext-mongodb instead + * deprecated `MemcacheSessionHandler`; use `MemcachedSessionHandler` instead + +3.3.0 +----- + + * the `Request::setTrustedProxies()` method takes a new `$trustedHeaderSet` argument, + see http://symfony.com/doc/current/components/http_foundation/trusting_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 + * added the `Cookie::fromString()` method that allows to create a cookie from a + raw header string + +3.1.0 +----- + + * Added support for creating `JsonResponse` with a string of JSON data + +3.0.0 +----- + + * The precedence of parameters returned from `Request::get()` changed from "GET, PATH, BODY" to "PATH, GET, BODY" + +2.8.0 +----- + + * Finding deep items in `ParameterBag::get()` is deprecated since version 2.8 and + will be removed in 3.0. + +2.6.0 +----- + + * PdoSessionHandler changes + - implemented different session locking strategies to prevent loss of data by concurrent access to the same session + - [BC BREAK] save session data in a binary column without base64_encode + - [BC BREAK] added lifetime column to the session table which allows to have different lifetimes for each session + - implemented lazy connections that are only opened when a session is used by either passing a dsn string + explicitly or falling back to session.save_path ini setting + - added a createTable method that initializes a correctly defined table depending on the database vendor + +2.5.0 +----- + + * added `JsonResponse::setEncodingOptions()` & `JsonResponse::getEncodingOptions()` for easier manipulation + of the options used while encoding data to JSON format. + +2.4.0 +----- + + * added RequestStack + * added Request::getEncodings() + * added accessors methods to session handlers + +2.3.0 +----- + + * added support for ranges of IPs in trusted proxies + * `UploadedFile::isValid` now returns false if the file was not uploaded via HTTP (in a non-test mode) + * Improved error-handling of `\Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler` + to ensure the supplied PDO handler throws Exceptions on error (as the class expects). Added related test cases + to verify that Exceptions are properly thrown when the PDO queries fail. + +2.2.0 +----- + + * fixed the Request::create() precedence (URI information always take precedence now) + * added Request::getTrustedProxies() + * deprecated Request::isProxyTrusted() + * [BC BREAK] JsonResponse does not turn a top level empty array to an object anymore, use an ArrayObject to enforce objects + * added a IpUtils class to check if an IP belongs to a CIDR + * added Request::getRealMethod() to get the "real" HTTP method (getMethod() returns the "intended" HTTP method) + * disabled _method request parameter support by default (call Request::enableHttpMethodParameterOverride() to + enable it, and Request::getHttpMethodParameterOverride() to check if it is supported) + * Request::splitHttpAcceptHeader() method is deprecated and will be removed in 2.3 + * Deprecated Flashbag::count() and \Countable interface, will be removed in 2.3 + +2.1.0 +----- + + * added Request::getSchemeAndHttpHost() and Request::getUserInfo() + * added a fluent interface to the Response class + * added Request::isProxyTrusted() + * added JsonResponse + * added a getTargetUrl method to RedirectResponse + * added support for streamed responses + * made Response::prepare() method the place to enforce HTTP specification + * [BC BREAK] moved management of the locale from the Session class to the Request class + * added a generic access to the PHP built-in filter mechanism: ParameterBag::filter() + * made FileBinaryMimeTypeGuesser command configurable + * added Request::getUser() and Request::getPassword() + * added support for the PATCH method in Request + * removed the ContentTypeMimeTypeGuesser class as it is deprecated and never used on PHP 5.3 + * added ResponseHeaderBag::makeDisposition() (implements RFC 6266) + * made mimetype to extension conversion configurable + * [BC BREAK] Moved all session related classes and interfaces into own namespace, as + `Symfony\Component\HttpFoundation\Session` and renamed classes accordingly. + Session handlers are located in the subnamespace `Symfony\Component\HttpFoundation\Session\Handler`. + * SessionHandlers must implement `\SessionHandlerInterface` or extend from the + `Symfony\Component\HttpFoundation\Storage\Handler\NativeSessionHandler` base class. + * Added internal storage driver proxy mechanism for forward compatibility with + PHP 5.4 `\SessionHandler` class. + * Added session handlers for custom Memcache, Memcached and Null session save handlers. + * [BC BREAK] Removed `NativeSessionStorage` and replaced with `NativeFileSessionHandler`. + * [BC BREAK] `SessionStorageInterface` methods removed: `write()`, `read()` and + `remove()`. Added `getBag()`, `registerBag()`. The `NativeSessionStorage` class + is a mediator for the session storage internals including the session handlers + which do the real work of participating in the internal PHP session workflow. + * [BC BREAK] Introduced mock implementations of `SessionStorage` to enable unit + and functional testing without starting real PHP sessions. Removed + `ArraySessionStorage`, and replaced with `MockArraySessionStorage` for unit + tests; removed `FilesystemSessionStorage`, and replaced with`MockFileSessionStorage` + for functional tests. These do not interact with global session ini + configuration values, session functions or `$_SESSION` superglobal. This means + they can be configured directly allowing multiple instances to work without + conflicting in the same PHP process. + * [BC BREAK] Removed the `close()` method from the `Session` class, as this is + now redundant. + * Deprecated the following methods from the Session class: `setFlash()`, `setFlashes()` + `getFlash()`, `hasFlash()`, and `removeFlash()`. Use `getFlashBag()` instead + which returns a `FlashBagInterface`. + * `Session->clear()` now only clears session attributes as before it cleared + flash messages and attributes. `Session->getFlashBag()->all()` clears flashes now. + * Session data is now managed by `SessionBagInterface` to better encapsulate + session data. + * Refactored session attribute and flash messages system to their own + `SessionBagInterface` implementations. + * 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 + be retrieved by `get()` or `all()`. + * Added `Symfony\Component\HttpFoundation\Attribute\AttributeBag` to replicate + attributes storage behaviour 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 + per flash type. The old `Session` class API remains without BC break as it + will allow single messages as before. + * Added basic session meta-data to the session to record session create time, + last updated time, and the lifetime of the session cookie that was provided + to the client. + * Request::getClientIp() method doesn't take a parameter anymore but bases + itself on the trustProxy parameter. + * Added isMethod() to Request object. + * [BC BREAK] The methods `getPathInfo()`, `getBaseUrl()` and `getBasePath()` of + a `Request` now all return a raw value (vs a urldecoded value before). Any call + to one of these methods must be checked and wrapped in a `rawurldecode()` if + needed. diff --git a/lib/silex/vendor/symfony/http-foundation/Cookie.php b/lib/silex/vendor/symfony/http-foundation/Cookie.php new file mode 100644 index 000000000..4519a6ada --- /dev/null +++ b/lib/silex/vendor/symfony/http-foundation/Cookie.php @@ -0,0 +1,289 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation; + +/** + * Represents a cookie. + * + * @author Johannes M. Schmitt + */ +class Cookie +{ + protected $name; + protected $value; + protected $domain; + protected $expire; + protected $path; + protected $secure; + protected $httpOnly; + private $raw; + private $sameSite; + + const SAMESITE_LAX = 'lax'; + const SAMESITE_STRICT = 'strict'; + + /** + * Creates cookie from raw header string. + * + * @param string $cookie + * @param bool $decode + * + * @return static + */ + public static function fromString($cookie, $decode = false) + { + $data = array( + 'expires' => 0, + 'path' => '/', + 'domain' => null, + 'secure' => 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; + } + } + + return new static($data['name'], $data['value'], $data['expires'], $data['path'], $data['domain'], $data['secure'], $data['httponly'], $data['raw'], $data['samesite']); + } + + /** + * @param string $name The name of the cookie + * @param string|null $value The value of the cookie + * @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 $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) + { + // from PHP source code + if (preg_match("/[=,; \t\r\n\013\014]/", $name)) { + throw new \InvalidArgumentException(sprintf('The cookie name "%s" contains invalid characters.', $name)); + } + + if (empty($name)) { + throw new \InvalidArgumentException('The cookie name cannot be empty.'); + } + + // convert expiration time to a Unix timestamp + if ($expire instanceof \DateTimeInterface) { + $expire = $expire->format('U'); + } elseif (!is_numeric($expire)) { + $expire = strtotime($expire); + + if (false === $expire) { + throw new \InvalidArgumentException('The cookie expiration time is not valid.'); + } + } + + $this->name = $name; + $this->value = $value; + $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; + + if (null !== $sameSite) { + $sameSite = strtolower($sameSite); + } + + if (!in_array($sameSite, array(self::SAMESITE_LAX, self::SAMESITE_STRICT, null), true)) { + throw new \InvalidArgumentException('The "sameSite" parameter value is not valid.'); + } + + $this->sameSite = $sameSite; + } + + /** + * Returns the cookie as a string. + * + * @return string The cookie + */ + public function __toString() + { + $str = ($this->isRaw() ? $this->getName() : urlencode($this->getName())).'='; + + if ('' === (string) $this->getValue()) { + $str .= 'deleted; expires='.gmdate('D, d-M-Y H:i:s T', time() - 31536001).'; max-age=-31536001'; + } else { + $str .= $this->isRaw() ? $this->getValue() : rawurlencode($this->getValue()); + + if (0 !== $this->getExpiresTime()) { + $str .= '; expires='.gmdate('D, d-M-Y H:i:s T', $this->getExpiresTime()).'; max-age='.$this->getMaxAge(); + } + } + + if ($this->getPath()) { + $str .= '; path='.$this->getPath(); + } + + if ($this->getDomain()) { + $str .= '; domain='.$this->getDomain(); + } + + if (true === $this->isSecure()) { + $str .= '; secure'; + } + + if (true === $this->isHttpOnly()) { + $str .= '; httponly'; + } + + if (null !== $this->getSameSite()) { + $str .= '; samesite='.$this->getSameSite(); + } + + return $str; + } + + /** + * Gets the name of the cookie. + * + * @return string + */ + public function getName() + { + return $this->name; + } + + /** + * Gets the value of the cookie. + * + * @return string|null + */ + public function getValue() + { + return $this->value; + } + + /** + * Gets the domain that the cookie is available to. + * + * @return string|null + */ + public function getDomain() + { + return $this->domain; + } + + /** + * Gets the time the cookie expires. + * + * @return int + */ + public function getExpiresTime() + { + return $this->expire; + } + + /** + * Gets the max-age attribute. + * + * @return int + */ + public function getMaxAge() + { + return 0 !== $this->expire ? $this->expire - time() : 0; + } + + /** + * Gets the path on the server in which the cookie will be available on. + * + * @return string + */ + public function getPath() + { + return $this->path; + } + + /** + * Checks whether the cookie should only be transmitted over a secure HTTPS connection from the client. + * + * @return bool + */ + public function isSecure() + { + return $this->secure; + } + + /** + * Checks whether the cookie will be made accessible only through the HTTP protocol. + * + * @return bool + */ + public function isHttpOnly() + { + return $this->httpOnly; + } + + /** + * Whether this cookie is about to be cleared. + * + * @return bool + */ + public function isCleared() + { + return $this->expire < time(); + } + + /** + * Checks if the cookie value should be sent with no url encoding. + * + * @return bool + */ + public function isRaw() + { + return $this->raw; + } + + /** + * Gets the SameSite attribute. + * + * @return string|null + */ + public function getSameSite() + { + return $this->sameSite; + } +} diff --git a/lib/silex/vendor/symfony/http-foundation/Exception/ConflictingHeadersException.php b/lib/silex/vendor/symfony/http-foundation/Exception/ConflictingHeadersException.php new file mode 100644 index 000000000..5fcf5b426 --- /dev/null +++ b/lib/silex/vendor/symfony/http-foundation/Exception/ConflictingHeadersException.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\Exception; + +/** + * The HTTP request contains headers with conflicting information. + * + * @author Magnus Nordlander + */ +class ConflictingHeadersException extends \UnexpectedValueException implements RequestExceptionInterface +{ +} diff --git a/lib/silex/vendor/symfony/http-foundation/Exception/RequestExceptionInterface.php b/lib/silex/vendor/symfony/http-foundation/Exception/RequestExceptionInterface.php new file mode 100644 index 000000000..478d0dc7e --- /dev/null +++ b/lib/silex/vendor/symfony/http-foundation/Exception/RequestExceptionInterface.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\Exception; + +/** + * Interface for Request exceptions. + * + * Exceptions implementing this interface should trigger an HTTP 400 response in the application code. + */ +interface RequestExceptionInterface +{ +} diff --git a/lib/silex/vendor/symfony/http-foundation/Exception/SuspiciousOperationException.php b/lib/silex/vendor/symfony/http-foundation/Exception/SuspiciousOperationException.php new file mode 100644 index 000000000..ae7a5f133 --- /dev/null +++ b/lib/silex/vendor/symfony/http-foundation/Exception/SuspiciousOperationException.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\HttpFoundation\Exception; + +/** + * Raised when a user has performed an operation that should be considered + * suspicious from a security perspective. + */ +class SuspiciousOperationException extends \UnexpectedValueException implements RequestExceptionInterface +{ +} diff --git a/lib/silex/vendor/symfony/http-foundation/ExpressionRequestMatcher.php b/lib/silex/vendor/symfony/http-foundation/ExpressionRequestMatcher.php new file mode 100644 index 000000000..e9c8441ce --- /dev/null +++ b/lib/silex/vendor/symfony/http-foundation/ExpressionRequestMatcher.php @@ -0,0 +1,47 @@ + + * + * 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\ExpressionLanguage\ExpressionLanguage; + +/** + * ExpressionRequestMatcher uses an expression to match a Request. + * + * @author Fabien Potencier + */ +class ExpressionRequestMatcher extends RequestMatcher +{ + private $language; + private $expression; + + public function setExpression(ExpressionLanguage $language, $expression) + { + $this->language = $language; + $this->expression = $expression; + } + + public function matches(Request $request) + { + if (!$this->language) { + throw new \LogicException('Unable to match the request as the expression language is not available.'); + } + + return $this->language->evaluate($this->expression, array( + 'request' => $request, + 'method' => $request->getMethod(), + 'path' => rawurldecode($request->getPathInfo()), + 'host' => $request->getHost(), + 'ip' => $request->getClientIp(), + 'attributes' => $request->attributes->all(), + )) && parent::matches($request); + } +} diff --git a/lib/silex/vendor/symfony/http-foundation/File/Exception/AccessDeniedException.php b/lib/silex/vendor/symfony/http-foundation/File/Exception/AccessDeniedException.php new file mode 100644 index 000000000..3b8e41d4a --- /dev/null +++ b/lib/silex/vendor/symfony/http-foundation/File/Exception/AccessDeniedException.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\HttpFoundation\File\Exception; + +/** + * Thrown when the access on a file was denied. + * + * @author Bernhard Schussek + */ +class AccessDeniedException extends FileException +{ + /** + * @param string $path The path to the accessed file + */ + public function __construct($path) + { + parent::__construct(sprintf('The file %s could not be accessed', $path)); + } +} diff --git a/lib/silex/vendor/symfony/http-foundation/File/Exception/FileException.php b/lib/silex/vendor/symfony/http-foundation/File/Exception/FileException.php new file mode 100644 index 000000000..fad5133e1 --- /dev/null +++ b/lib/silex/vendor/symfony/http-foundation/File/Exception/FileException.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 error occurred in the component File. + * + * @author Bernhard Schussek + */ +class FileException extends \RuntimeException +{ +} diff --git a/lib/silex/vendor/symfony/http-foundation/File/Exception/FileNotFoundException.php b/lib/silex/vendor/symfony/http-foundation/File/Exception/FileNotFoundException.php new file mode 100644 index 000000000..bfcc37ec6 --- /dev/null +++ b/lib/silex/vendor/symfony/http-foundation/File/Exception/FileNotFoundException.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\HttpFoundation\File\Exception; + +/** + * Thrown when a file was not found. + * + * @author Bernhard Schussek + */ +class FileNotFoundException extends FileException +{ + /** + * @param string $path The path to the file that was not found + */ + public function __construct($path) + { + parent::__construct(sprintf('The file "%s" does not exist', $path)); + } +} diff --git a/lib/silex/vendor/symfony/http-foundation/File/Exception/UnexpectedTypeException.php b/lib/silex/vendor/symfony/http-foundation/File/Exception/UnexpectedTypeException.php new file mode 100644 index 000000000..0444b8778 --- /dev/null +++ b/lib/silex/vendor/symfony/http-foundation/File/Exception/UnexpectedTypeException.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\HttpFoundation\File\Exception; + +class UnexpectedTypeException extends FileException +{ + public function __construct($value, $expectedType) + { + parent::__construct(sprintf('Expected argument of type %s, %s given', $expectedType, is_object($value) ? get_class($value) : gettype($value))); + } +} diff --git a/lib/silex/vendor/symfony/http-foundation/File/Exception/UploadException.php b/lib/silex/vendor/symfony/http-foundation/File/Exception/UploadException.php new file mode 100644 index 000000000..7074e7653 --- /dev/null +++ b/lib/silex/vendor/symfony/http-foundation/File/Exception/UploadException.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 error occurred during file upload. + * + * @author Bernhard Schussek + */ +class UploadException extends FileException +{ +} diff --git a/lib/silex/vendor/symfony/http-foundation/File/File.php b/lib/silex/vendor/symfony/http-foundation/File/File.php new file mode 100644 index 000000000..e2a67684f --- /dev/null +++ b/lib/silex/vendor/symfony/http-foundation/File/File.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\HttpFoundation\File; + +use Symfony\Component\HttpFoundation\File\Exception\FileException; +use Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException; +use Symfony\Component\HttpFoundation\File\MimeType\MimeTypeGuesser; +use Symfony\Component\HttpFoundation\File\MimeType\ExtensionGuesser; + +/** + * A file in the file system. + * + * @author Bernhard Schussek + */ +class File extends \SplFileInfo +{ + /** + * Constructs a new file from the given path. + * + * @param string $path The path to the file + * @param bool $checkPath Whether to check the path or not + * + * @throws FileNotFoundException If the given path is not a file + */ + public function __construct($path, $checkPath = true) + { + if ($checkPath && !is_file($path)) { + throw new FileNotFoundException($path); + } + + parent::__construct($path); + } + + /** + * Returns the extension based on the mime type. + * + * If the mime type is unknown, returns null. + * + * This method uses the mime type as guessed by getMimeType() + * to guess the file extension. + * + * @return string|null The guessed extension or null if it cannot be guessed + * + * @see ExtensionGuesser + * @see getMimeType() + */ + public function guessExtension() + { + $type = $this->getMimeType(); + $guesser = ExtensionGuesser::getInstance(); + + return $guesser->guess($type); + } + + /** + * 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. + * + * @return string|null The guessed mime type (e.g. "application/pdf") + * + * @see MimeTypeGuesser + */ + public function getMimeType() + { + $guesser = MimeTypeGuesser::getInstance(); + + return $guesser->guess($this->getPathname()); + } + + /** + * Moves the file to a new location. + * + * @param string $directory The destination folder + * @param string $name The new file name + * + * @return self A File object representing the new file + * + * @throws FileException if the target file could not be created + */ + public function move($directory, $name = null) + { + $target = $this->getTargetFile($directory, $name); + + if (!@rename($this->getPathname(), $target)) { + $error = error_get_last(); + throw new FileException(sprintf('Could not move the file "%s" to "%s" (%s)', $this->getPathname(), $target, strip_tags($error['message']))); + } + + @chmod($target, 0666 & ~umask()); + + return $target; + } + + protected function getTargetFile($directory, $name = null) + { + if (!is_dir($directory)) { + if (false === @mkdir($directory, 0777, true) && !is_dir($directory)) { + throw new FileException(sprintf('Unable to create the "%s" directory', $directory)); + } + } elseif (!is_writable($directory)) { + throw new FileException(sprintf('Unable to write in the "%s" directory', $directory)); + } + + $target = rtrim($directory, '/\\').DIRECTORY_SEPARATOR.(null === $name ? $this->getBasename() : $this->getName($name)); + + return new self($target, false); + } + + /** + * Returns locale independent base name of the given path. + * + * @param string $name The new file name + * + * @return string containing + */ + protected function getName($name) + { + $originalName = str_replace('\\', '/', $name); + $pos = strrpos($originalName, '/'); + $originalName = false === $pos ? $originalName : substr($originalName, $pos + 1); + + return $originalName; + } +} diff --git a/lib/silex/vendor/symfony/http-foundation/File/MimeType/ExtensionGuesser.php b/lib/silex/vendor/symfony/http-foundation/File/MimeType/ExtensionGuesser.php new file mode 100644 index 000000000..263fb321c --- /dev/null +++ b/lib/silex/vendor/symfony/http-foundation/File/MimeType/ExtensionGuesser.php @@ -0,0 +1,94 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\File\MimeType; + +/** + * A singleton mime type to file extension guesser. + * + * A default guesser is provided. + * You can register custom guessers by calling the register() + * method on the singleton instance: + * + * $guesser = ExtensionGuesser::getInstance(); + * $guesser->register(new MyCustomExtensionGuesser()); + * + * The last registered guesser is preferred over previously registered ones. + */ +class ExtensionGuesser implements ExtensionGuesserInterface +{ + /** + * The singleton instance. + * + * @var ExtensionGuesser + */ + private static $instance = null; + + /** + * All registered ExtensionGuesserInterface instances. + * + * @var array + */ + protected $guessers = array(); + + /** + * Returns the singleton instance. + * + * @return self + */ + public static function getInstance() + { + if (null === self::$instance) { + self::$instance = new self(); + } + + return self::$instance; + } + + /** + * Registers all natively provided extension guessers. + */ + private function __construct() + { + $this->register(new MimeTypeExtensionGuesser()); + } + + /** + * Registers a new extension guesser. + * + * When guessing, this guesser is preferred over previously registered ones. + */ + public function register(ExtensionGuesserInterface $guesser) + { + array_unshift($this->guessers, $guesser); + } + + /** + * Tries to guess the extension. + * + * The mime type 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. + * + * @param string $mimeType The mime type + * + * @return string The guessed extension or NULL, if none could be guessed + */ + public function guess($mimeType) + { + foreach ($this->guessers as $guesser) { + if (null !== $extension = $guesser->guess($mimeType)) { + return $extension; + } + } + } +} diff --git a/lib/silex/vendor/symfony/http-foundation/File/MimeType/ExtensionGuesserInterface.php b/lib/silex/vendor/symfony/http-foundation/File/MimeType/ExtensionGuesserInterface.php new file mode 100644 index 000000000..d19a0e537 --- /dev/null +++ b/lib/silex/vendor/symfony/http-foundation/File/MimeType/ExtensionGuesserInterface.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\File\MimeType; + +/** + * Guesses the file extension corresponding to a given mime type. + */ +interface ExtensionGuesserInterface +{ + /** + * Makes a best guess for a file extension, given a mime type. + * + * @param string $mimeType The mime type + * + * @return string The guessed extension or NULL, if none could be guessed + */ + public function guess($mimeType); +} diff --git a/lib/silex/vendor/symfony/http-foundation/File/MimeType/FileBinaryMimeTypeGuesser.php b/lib/silex/vendor/symfony/http-foundation/File/MimeType/FileBinaryMimeTypeGuesser.php new file mode 100644 index 000000000..c2ac6768c --- /dev/null +++ b/lib/silex/vendor/symfony/http-foundation/File/MimeType/FileBinaryMimeTypeGuesser.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\File\MimeType; + +use Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException; +use Symfony\Component\HttpFoundation\File\Exception\AccessDeniedException; + +/** + * 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($cmd = 'file -b --mime %s 2>/dev/null') + { + $this->cmd = $cmd; + } + + /** + * Returns whether this guesser is supported on the current OS. + * + * @return bool + */ + public static function isSupported() + { + return '\\' !== DIRECTORY_SEPARATOR && function_exists('passthru') && function_exists('escapeshellarg'); + } + + /** + * {@inheritdoc} + */ + public function guess($path) + { + if (!is_file($path)) { + throw new FileNotFoundException($path); + } + + if (!is_readable($path)) { + throw new AccessDeniedException($path); + } + + if (!self::isSupported()) { + return; + } + + ob_start(); + + // need to use --mime instead of -i. see #6641 + passthru(sprintf($this->cmd, escapeshellarg($path)), $return); + if ($return > 0) { + ob_end_clean(); + + return; + } + + $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; + } + + return $match[1]; + } +} diff --git a/lib/silex/vendor/symfony/http-foundation/File/MimeType/FileinfoMimeTypeGuesser.php b/lib/silex/vendor/symfony/http-foundation/File/MimeType/FileinfoMimeTypeGuesser.php new file mode 100644 index 000000000..9b42835e4 --- /dev/null +++ b/lib/silex/vendor/symfony/http-foundation/File/MimeType/FileinfoMimeTypeGuesser.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\HttpFoundation\File\MimeType; + +use Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException; +use Symfony\Component\HttpFoundation\File\Exception\AccessDeniedException; + +/** + * 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($magicFile = null) + { + $this->magicFile = $magicFile; + } + + /** + * Returns whether this guesser is supported on the current OS/PHP setup. + * + * @return bool + */ + public static function isSupported() + { + return function_exists('finfo_open'); + } + + /** + * {@inheritdoc} + */ + public function guess($path) + { + if (!is_file($path)) { + throw new FileNotFoundException($path); + } + + if (!is_readable($path)) { + throw new AccessDeniedException($path); + } + + if (!self::isSupported()) { + return; + } + + if (!$finfo = new \finfo(FILEINFO_MIME_TYPE, $this->magicFile)) { + return; + } + + return $finfo->file($path); + } +} diff --git a/lib/silex/vendor/symfony/http-foundation/File/MimeType/MimeTypeExtensionGuesser.php b/lib/silex/vendor/symfony/http-foundation/File/MimeType/MimeTypeExtensionGuesser.php new file mode 100644 index 000000000..896c135a5 --- /dev/null +++ b/lib/silex/vendor/symfony/http-foundation/File/MimeType/MimeTypeExtensionGuesser.php @@ -0,0 +1,807 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\File\MimeType; + +/** + * Provides a best-guess mapping of mime type to file extension. + */ +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. + * + * @see http://svn.apache.org/repos/asf/httpd/httpd/trunk/docs/conf/mime.types + */ + protected $defaultExtensions = array( + 'application/andrew-inset' => 'ez', + '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/cu-seeme' => 'cu', + 'application/davmount+xml' => 'davmount', + 'application/docbook+xml' => 'dbk', + 'application/dssc+der' => 'dssc', + 'application/dssc+xml' => 'xdssc', + 'application/ecmascript' => 'ecma', + 'application/emma+xml' => 'emma', + 'application/epub+zip' => 'epub', + 'application/exi' => 'exi', + 'application/font-tdpfr' => 'pfr', + 'application/gml+xml' => 'gml', + 'application/gpx+xml' => 'gpx', + 'application/gxf' => 'gxf', + 'application/hyperstudio' => 'stk', + 'application/inkml+xml' => 'ink', + 'application/ipfix' => 'ipfix', + 'application/java-archive' => 'jar', + 'application/java-serialized-object' => 'ser', + 'application/java-vm' => 'class', + 'application/javascript' => 'js', + 'application/json' => 'json', + 'application/jsonml+json' => 'jsonml', + 'application/lost+xml' => 'lostxml', + 'application/mac-binhex40' => 'hqx', + 'application/mac-compactpro' => 'cpt', + 'application/mads+xml' => 'mads', + 'application/marc' => 'mrc', + 'application/marcxml+xml' => 'mrcx', + 'application/mathematica' => 'ma', + 'application/mathml+xml' => 'mathml', + 'application/mbox' => 'mbox', + 'application/mediaservercontrol+xml' => 'mscml', + 'application/metalink+xml' => 'metalink', + 'application/metalink4+xml' => 'meta4', + 'application/mets+xml' => 'mets', + 'application/mods+xml' => 'mods', + 'application/mp21' => 'm21', + 'application/mp4' => 'mp4s', + 'application/msword' => 'doc', + 'application/mxf' => 'mxf', + 'application/octet-stream' => 'bin', + 'application/oda' => 'oda', + 'application/oebps-package+xml' => 'opf', + 'application/ogg' => 'ogx', + 'application/omdoc+xml' => 'omdoc', + 'application/onenote' => 'onetoc', + 'application/oxps' => 'oxps', + 'application/patch-ops-error+xml' => 'xer', + 'application/pdf' => 'pdf', + 'application/pgp-encrypted' => 'pgp', + 'application/pgp-signature' => 'asc', + 'application/pics-rules' => 'prf', + 'application/pkcs10' => 'p10', + 'application/pkcs7-mime' => 'p7m', + 'application/pkcs7-signature' => 'p7s', + 'application/pkcs8' => 'p8', + 'application/pkix-attr-cert' => 'ac', + 'application/pkix-cert' => 'cer', + 'application/pkix-crl' => 'crl', + 'application/pkix-pkipath' => 'pkipath', + 'application/pkixcmp' => 'pki', + 'application/pls+xml' => 'pls', + 'application/postscript' => 'ai', + 'application/prs.cww' => 'cww', + 'application/pskc+xml' => 'pskcxml', + 'application/rdf+xml' => 'rdf', + '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/smil+xml' => 'smi', + 'application/sparql-query' => 'rq', + 'application/sparql-results+xml' => 'srx', + 'application/srgs' => 'gram', + 'application/srgs+xml' => 'grxml', + 'application/sru+xml' => 'sru', + 'application/ssdl+xml' => 'ssdl', + 'application/ssml+xml' => 'ssml', + 'application/tei+xml' => 'tei', + 'application/thraud+xml' => 'tfi', + 'application/timestamped-data' => 'tsd', + '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', + 'application/vnd.adobe.air-application-installer-package+zip' => 'air', + 'application/vnd.adobe.formscentral.fcdt' => 'fcdt', + 'application/vnd.adobe.fxp' => 'fxp', + '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.apple.installer+xml' => 'mpkg', + 'application/vnd.apple.mpegurl' => 'm3u8', + '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.chipnuts.karaoke-mmd' => 'mmd', + 'application/vnd.cinderella' => 'cdy', + 'application/vnd.claymore' => 'cla', + 'application/vnd.cloanto.rp9' => 'rp9', + 'application/vnd.clonk.c4group' => 'c4g', + 'application/vnd.cluetrust.cartomobile-config' => 'c11amc', + 'application/vnd.cluetrust.cartomobile-config-pkg' => 'c11amz', + 'application/vnd.commonspace' => 'csp', + 'application/vnd.contact.cmsg' => 'cdbcmsg', + '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.dece.data' => 'uvf', + 'application/vnd.dece.ttml+xml' => 'uvt', + 'application/vnd.dece.unspecified' => 'uvx', + 'application/vnd.dece.zip' => 'uvz', + '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.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', + 'application/vnd.ezpix-album' => 'ez2', + 'application/vnd.ezpix-package' => 'ez3', + 'application/vnd.fdf' => 'fdf', + 'application/vnd.fdsn.mseed' => 'mseed', + 'application/vnd.fdsn.seed' => 'seed', + 'application/vnd.flographit' => 'gph', + 'application/vnd.fluxtime.clip' => 'ftc', + 'application/vnd.framemaker' => 'fm', + '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.geogebra.file' => 'ggb', + 'application/vnd.geogebra.tool' => 'ggt', + 'application/vnd.geometry-explorer' => 'gex', + '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', + '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.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', + 'application/vnd.ibm.rights-management' => 'irm', + 'application/vnd.ibm.secure-container' => 'sc', + 'application/vnd.iccprofile' => 'icc', + 'application/vnd.igloader' => 'igl', + 'application/vnd.immervision-ivp' => 'ivp', + 'application/vnd.immervision-ivu' => 'ivu', + 'application/vnd.insors.igm' => 'igm', + 'application/vnd.intercon.formnet' => 'xpw', + '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', + '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', + 'application/vnd.kde.kspread' => 'ksp', + 'application/vnd.kde.kword' => 'kwd', + 'application/vnd.kenameaapp' => 'htke', + 'application/vnd.kidspiration' => 'kia', + 'application/vnd.kinar' => 'kne', + 'application/vnd.koan' => 'skp', + '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', + '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-artgalry' => 'cil', + 'application/vnd.ms-cab-compressed' => 'cab', + 'application/vnd.ms-excel' => 'xls', + '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', + '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', + 'application/vnd.ms-word.document.macroenabled.12' => 'docm', + 'application/vnd.ms-word.template.macroenabled.12' => 'dotm', + 'application/vnd.ms-works' => 'wps', + 'application/vnd.ms-wpl' => 'wpl', + 'application/vnd.ms-xpsdocument' => 'xps', + 'application/vnd.mseq' => 'mseq', + 'application/vnd.musician' => 'mus', + 'application/vnd.muvee.style' => 'msty', + 'application/vnd.mynfc' => 'taglet', + 'application/vnd.neurolanguage.nlu' => 'nlu', + 'application/vnd.nitf' => 'ntf', + '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.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', + 'application/vnd.oasis.opendocument.graphics' => 'odg', + '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-template' => 'otp', + 'application/vnd.oasis.opendocument.spreadsheet' => 'ods', + 'application/vnd.oasis.opendocument.spreadsheet-template' => 'ots', + 'application/vnd.oasis.opendocument.text' => 'odt', + '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', + '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', + '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', + 'application/vnd.rn-realmedia-vbr' => 'rmvb', + 'application/vnd.route66.link66+xml' => 'link66', + 'application/vnd.sailingtracker.track' => 'st', + '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', + 'application/vnd.smaf' => 'mmf', + 'application/vnd.smart.teacher' => 'teacher', + 'application/vnd.solent.sdkm+xml' => 'sdkm', + 'application/vnd.spotfire.dxp' => 'dxp', + 'application/vnd.spotfire.sfs' => 'sfs', + 'application/vnd.stardivision.calc' => 'sdc', + 'application/vnd.stardivision.draw' => 'sda', + 'application/vnd.stardivision.impress' => 'sdd', + 'application/vnd.stardivision.math' => 'smf', + 'application/vnd.stardivision.writer' => 'sdw', + 'application/vnd.stardivision.writer-global' => 'sgl', + 'application/vnd.stepmania.package' => 'smzip', + 'application/vnd.stepmania.stepchart' => 'sm', + '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', + 'application/vnd.svd' => 'svd', + 'application/vnd.symbian.install' => 'sis', + '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', + 'application/vnd.tmobile-livetv' => 'tmo', + 'application/vnd.trid.tpt' => 'tpt', + 'application/vnd.triscape.mxs' => 'mxs', + 'application/vnd.trueapp' => 'tra', + 'application/vnd.ufdl' => 'ufd', + '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', + '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', + 'application/vnd.wqd' => 'wqd', + 'application/vnd.wt.stf' => 'stf', + 'application/vnd.xara' => 'xar', + '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.zul' => 'zir', + 'application/vnd.zzazz.deck+xml' => 'zaz', + 'application/voicexml+xml' => 'vxml', + 'application/widget' => 'wgt', + 'application/winhlp' => 'hlp', + 'application/wsdl+xml' => 'wsdl', + 'application/wspolicy+xml' => 'wspolicy', + 'application/x-7z-compressed' => '7z', + 'application/x-abiword' => 'abw', + 'application/x-ace-compressed' => 'ace', + 'application/x-apple-diskimage' => 'dmg', + 'application/x-authorware-bin' => 'aab', + 'application/x-authorware-map' => 'aam', + 'application/x-authorware-seg' => 'aas', + 'application/x-bcpio' => 'bcpio', + 'application/x-bittorrent' => 'torrent', + 'application/x-blorb' => 'blb', + 'application/x-bzip' => 'bz', + 'application/x-bzip2' => 'bz2', + 'application/x-cbr' => 'cbr', + 'application/x-cdlink' => 'vcd', + 'application/x-cfs-compressed' => 'cfs', + 'application/x-chat' => 'chat', + 'application/x-chess-pgn' => 'pgn', + 'application/x-conference' => 'nsc', + 'application/x-cpio' => 'cpio', + 'application/x-csh' => 'csh', + 'application/x-debian-package' => 'deb', + 'application/x-dgc-compressed' => 'dgc', + 'application/x-director' => 'dir', + 'application/x-doom' => 'wad', + 'application/x-dtbncx+xml' => 'ncx', + 'application/x-dtbook+xml' => 'dtb', + 'application/x-dtbresource+xml' => 'res', + 'application/x-dvi' => 'dvi', + 'application/x-envoy' => 'evy', + 'application/x-eva' => 'eva', + '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', + 'application/x-font-snf' => 'snf', + 'application/x-font-ttf' => 'ttf', + 'application/x-font-type1' => 'pfa', + 'application/x-font-woff' => 'woff', + 'application/x-freearc' => 'arc', + 'application/x-futuresplash' => 'spl', + 'application/x-gca-compressed' => 'gca', + 'application/x-glulx' => 'ulx', + 'application/x-gnumeric' => 'gnumeric', + 'application/x-gramps-xml' => 'gramps', + 'application/x-gtar' => 'gtar', + 'application/x-hdf' => 'hdf', + 'application/x-install-instructions' => 'install', + 'application/x-iso9660-image' => 'iso', + 'application/x-java-jnlp-file' => 'jnlp', + 'application/x-latex' => 'latex', + 'application/x-lzh-compressed' => 'lzh', + 'application/x-mie' => 'mie', + 'application/x-mobipocket-ebook' => 'prc', + 'application/x-ms-application' => 'application', + 'application/x-ms-shortcut' => 'lnk', + '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', + 'application/x-msmediaview' => 'mvb', + 'application/x-msmetafile' => 'wmf', + 'application/x-msmoney' => 'mny', + 'application/x-mspublisher' => 'pub', + 'application/x-msschedule' => 'scd', + 'application/x-msterminal' => 'trm', + 'application/x-mswrite' => 'wri', + 'application/x-netcdf' => 'nc', + 'application/x-nzb' => 'nzb', + 'application/x-pkcs12' => 'p12', + 'application/x-pkcs7-certificates' => 'p7b', + 'application/x-pkcs7-certreqresp' => 'p7r', + 'application/x-rar-compressed' => 'rar', + 'application/x-rar' => 'rar', + 'application/x-research-info-systems' => 'ris', + 'application/x-sh' => 'sh', + 'application/x-shar' => 'shar', + 'application/x-shockwave-flash' => 'swf', + 'application/x-silverlight-app' => 'xap', + 'application/x-sql' => 'sql', + '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-tads' => 'gam', + 'application/x-tar' => 'tar', + 'application/x-tcl' => 'tcl', + 'application/x-tex' => 'tex', + 'application/x-tex-tfm' => 'tfm', + 'application/x-texinfo' => 'texinfo', + 'application/x-tgif' => 'obj', + 'application/x-ustar' => 'ustar', + 'application/x-wais-source' => 'src', + 'application/x-x509-ca-cert' => 'der', + 'application/x-xfig' => 'fig', + 'application/x-xliff+xml' => 'xlf', + 'application/x-xpinstall' => 'xpi', + 'application/x-xz' => 'xz', + 'application/x-zmachine' => 'z1', + 'application/xaml+xml' => 'xaml', + 'application/xcap-diff+xml' => 'xdf', + 'application/xenc+xml' => 'xenc', + 'application/xhtml+xml' => 'xhtml', + 'application/xml' => 'xml', + 'application/xml-dtd' => 'dtd', + 'application/xop+xml' => 'xop', + 'application/xproc+xml' => 'xpl', + 'application/xslt+xml' => 'xslt', + 'application/xspf+xml' => 'xspf', + 'application/xv+xml' => 'mxml', + 'application/yang' => 'yang', + 'application/yin+xml' => 'yin', + 'application/zip' => 'zip', + 'audio/adpcm' => 'adp', + 'audio/basic' => 'au', + 'audio/midi' => 'mid', + 'audio/mp4' => 'mp4a', + 'audio/mpeg' => 'mpga', + 'audio/ogg' => 'oga', + 'audio/s3m' => 's3m', + 'audio/silk' => 'sil', + 'audio/vnd.dece.audio' => 'uva', + '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.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/webm' => 'weba', + 'audio/x-aac' => 'aac', + 'audio/x-aiff' => 'aif', + 'audio/x-caf' => 'caf', + 'audio/x-flac' => 'flac', + 'audio/x-matroska' => 'mka', + 'audio/x-mpegurl' => 'm3u', + 'audio/x-ms-wax' => 'wax', + 'audio/x-ms-wma' => 'wma', + 'audio/x-pn-realaudio' => 'ram', + 'audio/x-pn-realaudio-plugin' => 'rmp', + 'audio/x-wav' => 'wav', + 'audio/xm' => 'xm', + 'chemical/x-cdx' => 'cdx', + 'chemical/x-cif' => 'cif', + 'chemical/x-cmdf' => 'cmdf', + 'chemical/x-cml' => 'cml', + 'chemical/x-csml' => 'csml', + 'chemical/x-xyz' => 'xyz', + 'image/bmp' => 'bmp', + 'image/x-ms-bmp' => 'bmp', + 'image/cgm' => 'cgm', + 'image/g3fax' => 'g3', + 'image/gif' => 'gif', + 'image/ief' => 'ief', + 'image/jpeg' => 'jpeg', + 'image/pjpeg' => 'jpeg', + 'image/ktx' => 'ktx', + 'image/png' => 'png', + 'image/prs.btif' => 'btif', + 'image/sgi' => 'sgi', + 'image/svg+xml' => 'svg', + 'image/tiff' => 'tiff', + 'image/vnd.adobe.photoshop' => 'psd', + 'image/vnd.dece.graphic' => 'uvi', + 'image/vnd.dvb.subtitle' => 'sub', + 'image/vnd.djvu' => 'djvu', + '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.ms-modi' => 'mdi', + 'image/vnd.ms-photo' => 'wdp', + 'image/vnd.net-fpx' => 'npx', + 'image/vnd.wap.wbmp' => 'wbmp', + 'image/vnd.xiff' => 'xif', + 'image/webp' => 'webp', + 'image/x-3ds' => '3ds', + 'image/x-cmu-raster' => 'ras', + 'image/x-cmx' => 'cmx', + 'image/x-freehand' => 'fh', + 'image/x-icon' => 'ico', + 'image/x-mrsid-image' => 'sid', + 'image/x-pcx' => 'pcx', + 'image/x-pict' => 'pic', + 'image/x-portable-anymap' => 'pnm', + 'image/x-portable-bitmap' => 'pbm', + 'image/x-portable-graymap' => 'pgm', + 'image/x-portable-pixmap' => 'ppm', + 'image/x-rgb' => 'rgb', + 'image/x-tga' => 'tga', + 'image/x-xbitmap' => 'xbm', + 'image/x-xpixmap' => 'xpm', + 'image/x-xwindowdump' => 'xwd', + 'message/rfc822' => 'eml', + 'model/iges' => 'igs', + 'model/mesh' => 'msh', + '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', + 'model/x3d+binary' => 'x3db', + 'model/x3d+vrml' => 'x3dv', + 'model/x3d+xml' => 'x3d', + 'text/cache-manifest' => 'appcache', + 'text/calendar' => 'ics', + 'text/css' => 'css', + 'text/csv' => 'csv', + 'text/html' => 'html', + 'text/n3' => 'n3', + 'text/plain' => 'txt', + 'text/prs.lines.tag' => 'dsc', + 'text/richtext' => 'rtx', + 'text/rtf' => 'rtf', + 'text/sgml' => 'sgml', + 'text/tab-separated-values' => 'tsv', + 'text/troff' => 't', + 'text/turtle' => 'ttl', + 'text/uri-list' => 'uri', + 'text/vcard' => 'vcard', + 'text/vnd.curl' => 'curl', + 'text/vnd.curl.dcurl' => 'dcurl', + 'text/vnd.curl.scurl' => 'scurl', + 'text/vnd.curl.mcurl' => 'mcurl', + 'text/vnd.dvb.subtitle' => 'sub', + 'text/vnd.fly' => 'fly', + 'text/vnd.fmi.flexstor' => 'flx', + 'text/vnd.graphviz' => 'gv', + 'text/vnd.in3d.3dml' => '3dml', + 'text/vnd.in3d.spot' => 'spot', + 'text/vnd.sun.j2me.app-descriptor' => 'jad', + 'text/vnd.wap.wml' => 'wml', + 'text/vnd.wap.wmlscript' => 'wmls', + 'text/vtt' => 'vtt', + '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-setext' => 'etx', + 'text/x-sfv' => 'sfv', + 'text/x-uuencode' => 'uu', + 'text/x-vcalendar' => 'vcs', + 'text/x-vcard' => 'vcf', + 'video/3gpp' => '3gp', + 'video/3gpp2' => '3g2', + 'video/h261' => 'h261', + 'video/h263' => 'h263', + 'video/h264' => 'h264', + 'video/jpeg' => 'jpgv', + 'video/jpm' => 'jpm', + 'video/mj2' => 'mj2', + 'video/mp4' => 'mp4', + 'video/mpeg' => 'mpeg', + 'video/ogg' => 'ogv', + 'video/quicktime' => 'qt', + 'video/vnd.dece.hd' => 'uvh', + 'video/vnd.dece.mobile' => 'uvm', + 'video/vnd.dece.pd' => 'uvp', + 'video/vnd.dece.sd' => 'uvs', + 'video/vnd.dece.video' => 'uvv', + 'video/vnd.dvb.file' => 'dvb', + 'video/vnd.fvt' => 'fvt', + 'video/vnd.mpegurl' => 'mxu', + 'video/vnd.ms-playready.media.pyv' => 'pyv', + 'video/vnd.uvvu.mp4' => 'uvu', + 'video/vnd.vivo' => 'viv', + 'video/webm' => 'webm', + 'video/x-f4v' => 'f4v', + 'video/x-fli' => 'fli', + 'video/x-flv' => 'flv', + 'video/x-m4v' => 'm4v', + 'video/x-matroska' => 'mkv', + 'video/x-mng' => 'mng', + 'video/x-ms-asf' => 'asf', + 'video/x-ms-vob' => 'vob', + 'video/x-ms-wm' => 'wm', + 'video/x-ms-wmv' => 'wmv', + 'video/x-ms-wmx' => 'wmx', + 'video/x-ms-wvx' => 'wvx', + 'video/x-msvideo' => 'avi', + '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; + } +} diff --git a/lib/silex/vendor/symfony/http-foundation/File/MimeType/MimeTypeGuesser.php b/lib/silex/vendor/symfony/http-foundation/File/MimeType/MimeTypeGuesser.php new file mode 100644 index 000000000..e3ef45ef6 --- /dev/null +++ b/lib/silex/vendor/symfony/http-foundation/File/MimeType/MimeTypeGuesser.php @@ -0,0 +1,142 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\File\MimeType; + +use Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException; +use Symfony\Component\HttpFoundation\File\Exception\AccessDeniedException; + +/** + * A singleton mime type guesser. + * + * By default, all mime type guessers provided by the framework are installed + * (if available on the current OS/PHP setup). + * + * You can register custom guessers by calling the register() method on the + * singleton instance. Custom guessers are always called before any default ones. + * + * $guesser = MimeTypeGuesser::getInstance(); + * $guesser->register(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 = MimeTypeGuesser::getInstance(); + * $guesser->register(new FileinfoMimeTypeGuesser('/path/to/magic/file')); + * + * @author Bernhard Schussek + */ +class MimeTypeGuesser implements MimeTypeGuesserInterface +{ + /** + * The singleton instance. + * + * @var MimeTypeGuesser + */ + private static $instance = null; + + /** + * All registered MimeTypeGuesserInterface instances. + * + * @var array + */ + protected $guessers = array(); + + /** + * Returns the singleton instance. + * + * @return self + */ + public static function getInstance() + { + if (null === self::$instance) { + self::$instance = new self(); + } + + return self::$instance; + } + + /** + * Resets the singleton instance. + */ + public static function reset() + { + self::$instance = null; + } + + /** + * Registers all natively provided mime type guessers. + */ + private function __construct() + { + if (FileBinaryMimeTypeGuesser::isSupported()) { + $this->register(new FileBinaryMimeTypeGuesser()); + } + + if (FileinfoMimeTypeGuesser::isSupported()) { + $this->register(new FileinfoMimeTypeGuesser()); + } + } + + /** + * Registers a new mime type guesser. + * + * When guessing, this guesser is preferred over previously registered ones. + */ + public function register(MimeTypeGuesserInterface $guesser) + { + array_unshift($this->guessers, $guesser); + } + + /** + * Tries to guess the mime type of the given file. + * + * 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. + * + * @param string $path The path to the file + * + * @return string The mime type or NULL, if none could be guessed + * + * @throws \LogicException + * @throws FileNotFoundException + * @throws AccessDeniedException + */ + public function guess($path) + { + if (!is_file($path)) { + throw new FileNotFoundException($path); + } + + if (!is_readable($path)) { + throw new AccessDeniedException($path); + } + + if (!$this->guessers) { + $msg = 'Unable to guess the mime type as no guessers are available'; + if (!FileinfoMimeTypeGuesser::isSupported()) { + $msg .= ' (Did you enable the php_fileinfo extension?)'; + } + throw new \LogicException($msg); + } + + foreach ($this->guessers as $guesser) { + if (null !== $mimeType = $guesser->guess($path)) { + return $mimeType; + } + } + } +} diff --git a/lib/silex/vendor/symfony/http-foundation/File/MimeType/MimeTypeGuesserInterface.php b/lib/silex/vendor/symfony/http-foundation/File/MimeType/MimeTypeGuesserInterface.php new file mode 100644 index 000000000..f8c3ad228 --- /dev/null +++ b/lib/silex/vendor/symfony/http-foundation/File/MimeType/MimeTypeGuesserInterface.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\HttpFoundation\File\MimeType; + +use Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException; +use Symfony\Component\HttpFoundation\File\Exception\AccessDeniedException; + +/** + * Guesses the mime type of a file. + * + * @author Bernhard Schussek + */ +interface MimeTypeGuesserInterface +{ + /** + * Guesses the mime type of the file with the given path. + * + * @param string $path The path to the file + * + * @return string 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 + */ + public function guess($path); +} diff --git a/lib/silex/vendor/symfony/http-foundation/File/Stream.php b/lib/silex/vendor/symfony/http-foundation/File/Stream.php new file mode 100644 index 000000000..69ae74c11 --- /dev/null +++ b/lib/silex/vendor/symfony/http-foundation/File/Stream.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\HttpFoundation\File; + +/** + * A PHP stream of unknown size. + * + * @author Nicolas Grekas + */ +class Stream extends File +{ + /** + * {@inheritdoc} + */ + public function getSize() + { + return false; + } +} diff --git a/lib/silex/vendor/symfony/http-foundation/File/UploadedFile.php b/lib/silex/vendor/symfony/http-foundation/File/UploadedFile.php new file mode 100644 index 000000000..082d8d534 --- /dev/null +++ b/lib/silex/vendor/symfony/http-foundation/File/UploadedFile.php @@ -0,0 +1,266 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\File; + +use Symfony\Component\HttpFoundation\File\Exception\FileException; +use Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException; +use Symfony\Component\HttpFoundation\File\MimeType\ExtensionGuesser; + +/** + * A file uploaded through a form. + * + * @author Bernhard Schussek + * @author Florian Eckerstorfer + * @author Fabien Potencier + */ +class UploadedFile extends File +{ + private $test = false; + private $originalName; + private $mimeType; + private $size; + private $error; + + /** + * Accepts the information of the uploaded file as provided by the PHP global $_FILES. + * + * The file object is only created when the uploaded file is valid (i.e. when the + * isValid() method returns true). Otherwise the only methods that could be called + * on an UploadedFile instance are: + * + * * getClientOriginalName, + * * getClientMimeType, + * * isValid, + * * getError. + * + * Calling any other method on an non-valid instance will cause an unpredictable result. + * + * @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 + * + * @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) + { + $this->originalName = $this->getName($originalName); + $this->mimeType = $mimeType ?: 'application/octet-stream'; + $this->size = $size; + $this->error = $error ?: UPLOAD_ERR_OK; + $this->test = (bool) $test; + + parent::__construct($path, UPLOAD_ERR_OK === $this->error); + } + + /** + * Returns the original file name. + * + * It is extracted from the request from which the file has been uploaded. + * Then it should not be considered as a safe value. + * + * @return string|null The original name + */ + public function getClientOriginalName() + { + return $this->originalName; + } + + /** + * Returns the original file extension. + * + * It is extracted from the original file name that was uploaded. + * Then it should not be considered as a safe value. + * + * @return string The extension + */ + public function getClientOriginalExtension() + { + return pathinfo($this->originalName, PATHINFO_EXTENSION); + } + + /** + * Returns the file mime type. + * + * The client mime type is extracted from the request from which the file + * was uploaded, so it should not be considered as a safe value. + * + * For a trusted mime type, use getMimeType() instead (which guesses the mime + * type based on the file content). + * + * @return string|null The mime type + * + * @see getMimeType() + */ + public function getClientMimeType() + { + return $this->mimeType; + } + + /** + * Returns the extension based on the client mime type. + * + * If the mime type is unknown, returns null. + * + * This method uses the mime type as guessed by getClientMimeType() + * to guess the file extension. As such, the extension returned + * by this method cannot be trusted. + * + * For a trusted extension, use guessExtension() instead (which guesses + * the extension based on the guessed mime type for the file). + * + * @return string|null The guessed extension or null if it cannot be guessed + * + * @see guessExtension() + * @see getClientMimeType() + */ + public function guessClientExtension() + { + $type = $this->getClientMimeType(); + $guesser = ExtensionGuesser::getInstance(); + + return $guesser->guess($type); + } + + /** + * Returns the file size. + * + * 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 + */ + public function getClientSize() + { + return $this->size; + } + + /** + * Returns the upload error. + * + * If the upload was successful, the constant UPLOAD_ERR_OK is returned. + * Otherwise one of the other UPLOAD_ERR_XXX constants is returned. + * + * @return int The upload error + */ + public function getError() + { + return $this->error; + } + + /** + * Returns whether the file was uploaded successfully. + * + * @return bool True if the file has been uploaded with HTTP and no error occurred + */ + public function isValid() + { + $isOk = UPLOAD_ERR_OK === $this->error; + + return $this->test ? $isOk : $isOk && is_uploaded_file($this->getPathname()); + } + + /** + * Moves the file to a new location. + * + * @param string $directory The destination folder + * @param string $name The new file name + * + * @return File A File object representing the new file + * + * @throws FileException if, for any reason, the file could not have been moved + */ + public function move($directory, $name = null) + { + if ($this->isValid()) { + if ($this->test) { + return parent::move($directory, $name); + } + + $target = $this->getTargetFile($directory, $name); + + if (!@move_uploaded_file($this->getPathname(), $target)) { + $error = error_get_last(); + throw new FileException(sprintf('Could not move the file "%s" to "%s" (%s)', $this->getPathname(), $target, strip_tags($error['message']))); + } + + @chmod($target, 0666 & ~umask()); + + return $target; + } + + throw new FileException($this->getErrorMessage()); + } + + /** + * Returns the maximum size of an uploaded file as configured in php.ini. + * + * @return int The maximum size of an uploaded file in bytes + */ + public static function getMaxFilesize() + { + $iniMax = strtolower(ini_get('upload_max_filesize')); + + if ('' === $iniMax) { + return PHP_INT_MAX; + } + + $max = ltrim($iniMax, '+'); + if (0 === strpos($max, '0x')) { + $max = intval($max, 16); + } elseif (0 === strpos($max, '0')) { + $max = intval($max, 8); + } else { + $max = (int) $max; + } + + switch (substr($iniMax, -1)) { + case 't': $max *= 1024; + // no break + case 'g': $max *= 1024; + // no break + case 'm': $max *= 1024; + // no break + case 'k': $max *= 1024; + } + + return $max; + } + + /** + * Returns an informative upload error message. + * + * @return string The error message regarding the specified error code + */ + public function getErrorMessage() + { + static $errors = array( + 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.', + UPLOAD_ERR_NO_FILE => 'No file was uploaded.', + 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; + $message = isset($errors[$errorCode]) ? $errors[$errorCode] : 'The file "%s" was not uploaded due to an unknown error.'; + + return sprintf($message, $this->getClientOriginalName(), $maxFilesize); + } +} diff --git a/lib/silex/vendor/symfony/http-foundation/FileBag.php b/lib/silex/vendor/symfony/http-foundation/FileBag.php new file mode 100644 index 000000000..5edd0e621 --- /dev/null +++ b/lib/silex/vendor/symfony/http-foundation/FileBag.php @@ -0,0 +1,144 @@ + + * + * 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\HttpFoundation\File\UploadedFile; + +/** + * FileBag is a container for uploaded files. + * + * @author Fabien Potencier + * @author Bulat Shakirzyanov + */ +class FileBag extends ParameterBag +{ + private static $fileKeys = array('error', 'name', 'size', 'tmp_name', 'type'); + + /** + * @param array $parameters An array of HTTP files + */ + public function __construct(array $parameters = array()) + { + $this->replace($parameters); + } + + /** + * {@inheritdoc} + */ + public function replace(array $files = array()) + { + $this->parameters = array(); + $this->add($files); + } + + /** + * {@inheritdoc} + */ + public function set($key, $value) + { + if (!is_array($value) && !$value instanceof UploadedFile) { + throw new \InvalidArgumentException('An uploaded file must be an array or an instance of UploadedFile.'); + } + + parent::set($key, $this->convertFileInformation($value)); + } + + /** + * {@inheritdoc} + */ + public function add(array $files = array()) + { + foreach ($files as $key => $file) { + $this->set($key, $file); + } + } + + /** + * Converts uploaded files to UploadedFile instances. + * + * @param array|UploadedFile $file A (multi-dimensional) array of uploaded file information + * + * @return UploadedFile[]|UploadedFile|null A (multi-dimensional) array of UploadedFile instances + */ + protected function convertFileInformation($file) + { + if ($file instanceof UploadedFile) { + return $file; + } + + $file = $this->fixPhpFilesArray($file); + if (is_array($file)) { + $keys = array_keys($file); + sort($keys); + + if ($keys == self::$fileKeys) { + if (UPLOAD_ERR_NO_FILE == $file['error']) { + $file = null; + } else { + $file = new UploadedFile($file['tmp_name'], $file['name'], $file['type'], $file['size'], $file['error']); + } + } else { + $file = array_map(array($this, 'convertFileInformation'), $file); + if (array_keys($keys) === $keys) { + $file = array_filter($file); + } + } + } + + return $file; + } + + /** + * Fixes a malformed PHP $_FILES array. + * + * PHP has a bug that the format of the $_FILES array differs, depending on + * whether the uploaded file fields had normal field names or array-like + * field names ("normal" vs. "parent[child]"). + * + * This method fixes the array to look like the "normal" $_FILES array. + * + * It's safe to pass an already converted array, in which case this method + * just returns the original array unmodified. + * + * @return array + */ + protected function fixPhpFilesArray($data) + { + if (!is_array($data)) { + return $data; + } + + $keys = array_keys($data); + sort($keys); + + if (self::$fileKeys != $keys || !isset($data['name']) || !is_array($data['name'])) { + return $data; + } + + $files = $data; + foreach (self::$fileKeys as $k) { + unset($files[$k]); + } + + foreach ($data['name'] as $key => $name) { + $files[$key] = $this->fixPhpFilesArray(array( + '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/lib/silex/vendor/symfony/http-foundation/HeaderBag.php b/lib/silex/vendor/symfony/http-foundation/HeaderBag.php new file mode 100644 index 000000000..7aaa52ae5 --- /dev/null +++ b/lib/silex/vendor/symfony/http-foundation/HeaderBag.php @@ -0,0 +1,331 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation; + +/** + * HeaderBag is a container for HTTP headers. + * + * @author Fabien Potencier + */ +class HeaderBag implements \IteratorAggregate, \Countable +{ + protected $headers = array(); + protected $cacheControl = array(); + + /** + * @param array $headers An array of HTTP headers + */ + public function __construct(array $headers = array()) + { + foreach ($headers as $key => $values) { + $this->set($key, $values); + } + } + + /** + * Returns the headers as a string. + * + * @return string The headers + */ + public function __toString() + { + if (!$headers = $this->all()) { + return ''; + } + + ksort($headers); + $max = max(array_map('strlen', array_keys($headers))) + 1; + $content = ''; + foreach ($headers as $name => $values) { + $name = implode('-', array_map('ucfirst', explode('-', $name))); + foreach ($values as $value) { + $content .= sprintf("%-{$max}s %s\r\n", $name.':', $value); + } + } + + return $content; + } + + /** + * Returns the headers. + * + * @return array An array of headers + */ + public function all() + { + return $this->headers; + } + + /** + * Returns the parameter keys. + * + * @return array An array of parameter keys + */ + public function keys() + { + return array_keys($this->all()); + } + + /** + * Replaces the current HTTP headers by a new set. + * + * @param array $headers An array of HTTP headers + */ + public function replace(array $headers = array()) + { + $this->headers = array(); + $this->add($headers); + } + + /** + * Adds new headers the current HTTP headers set. + * + * @param array $headers An array of HTTP headers + */ + public function add(array $headers) + { + foreach ($headers as $key => $values) { + $this->set($key, $values); + } + } + + /** + * Returns a header value by name. + * + * @param string $key The header name + * @param string|string[] $default The default value + * @param bool $first Whether to return the first value or all header values + * + * @return string|string[] The first header value or default value if $first is true, an array of values otherwise + */ + public function get($key, $default = null, $first = true) + { + $key = str_replace('_', '-', strtolower($key)); + $headers = $this->all(); + + if (!array_key_exists($key, $headers)) { + if (null === $default) { + return $first ? null : array(); + } + + return $first ? $default : array($default); + } + + if ($first) { + return \count($headers[$key]) ? $headers[$key][0] : $default; + } + + return $headers[$key]; + } + + /** + * Sets a header by name. + * + * @param string $key The key + * @param string|string[] $values The value or an array of values + * @param bool $replace Whether to replace the actual value or not (true by default) + */ + public function set($key, $values, $replace = true) + { + $key = str_replace('_', '-', strtolower($key)); + + if (\is_array($values)) { + $values = array_values($values); + + if (true === $replace || !isset($this->headers[$key])) { + $this->headers[$key] = $values; + } else { + $this->headers[$key] = array_merge($this->headers[$key], $values); + } + } else { + if (true === $replace || !isset($this->headers[$key])) { + $this->headers[$key] = array($values); + } else { + $this->headers[$key][] = $values; + } + } + + if ('cache-control' === $key) { + $this->cacheControl = $this->parseCacheControl(implode(', ', $this->headers[$key])); + } + } + + /** + * Returns true if the HTTP header is defined. + * + * @param string $key The HTTP header + * + * @return bool true if the parameter exists, false otherwise + */ + public function has($key) + { + return array_key_exists(str_replace('_', '-', strtolower($key)), $this->all()); + } + + /** + * Returns true if the given HTTP header contains the given value. + * + * @param string $key The HTTP header name + * @param string $value The HTTP value + * + * @return bool true if the value is contained in the header, false otherwise + */ + public function contains($key, $value) + { + return in_array($value, $this->get($key, null, false)); + } + + /** + * Removes a header. + * + * @param string $key The HTTP header name + */ + public function remove($key) + { + $key = str_replace('_', '-', strtolower($key)); + + unset($this->headers[$key]); + + if ('cache-control' === $key) { + $this->cacheControl = array(); + } + } + + /** + * Returns the HTTP header value converted to a date. + * + * @param string $key The parameter key + * @param \DateTime $default The default value + * + * @return null|\DateTime The parsed DateTime or the default value if the header does not exist + * + * @throws \RuntimeException When the HTTP header is not parseable + */ + public function getDate($key, \DateTime $default = null) + { + if (null === $value = $this->get($key)) { + return $default; + } + + if (false === $date = \DateTime::createFromFormat(DATE_RFC2822, $value)) { + throw new \RuntimeException(sprintf('The %s HTTP header is not parseable (%s).', $key, $value)); + } + + return $date; + } + + /** + * Adds a custom Cache-Control directive. + * + * @param string $key The Cache-Control directive name + * @param mixed $value The Cache-Control directive value + */ + public function addCacheControlDirective($key, $value = true) + { + $this->cacheControl[$key] = $value; + + $this->set('Cache-Control', $this->getCacheControlHeader()); + } + + /** + * Returns true if the Cache-Control directive is defined. + * + * @param string $key The Cache-Control directive + * + * @return bool true if the directive exists, false otherwise + */ + public function hasCacheControlDirective($key) + { + return array_key_exists($key, $this->cacheControl); + } + + /** + * Returns a Cache-Control directive value by name. + * + * @param string $key The directive name + * + * @return mixed|null The directive value if defined, null otherwise + */ + public function getCacheControlDirective($key) + { + return array_key_exists($key, $this->cacheControl) ? $this->cacheControl[$key] : null; + } + + /** + * Removes a Cache-Control directive. + * + * @param string $key The Cache-Control directive + */ + public function removeCacheControlDirective($key) + { + unset($this->cacheControl[$key]); + + $this->set('Cache-Control', $this->getCacheControlHeader()); + } + + /** + * Returns an iterator for headers. + * + * @return \ArrayIterator An \ArrayIterator instance + */ + public function getIterator() + { + return new \ArrayIterator($this->headers); + } + + /** + * Returns the number of headers. + * + * @return int The number of headers + */ + public function count() + { + return count($this->headers); + } + + 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); + } + + /** + * Parses a Cache-Control HTTP header. + * + * @param string $header The value of the Cache-Control HTTP header + * + * @return array An array representing the attribute values + */ + 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); + } + + return $cacheControl; + } +} diff --git a/lib/silex/vendor/symfony/http-foundation/IpUtils.php b/lib/silex/vendor/symfony/http-foundation/IpUtils.php new file mode 100644 index 000000000..86d135b2d --- /dev/null +++ b/lib/silex/vendor/symfony/http-foundation/IpUtils.php @@ -0,0 +1,156 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation; + +/** + * Http utility functions. + * + * @author Fabien Potencier + */ +class IpUtils +{ + private static $checkedIps = array(); + + /** + * This class should not be instantiated. + */ + private function __construct() + { + } + + /** + * Checks if an IPv4 or IPv6 address is contained in the list of given IPs or subnets. + * + * @param string $requestIp IP to check + * @param string|array $ips List of IPs or subnets (can be a string if only a single one) + * + * @return bool Whether the IP is valid + */ + public static function checkIp($requestIp, $ips) + { + if (!is_array($ips)) { + $ips = array($ips); + } + + $method = substr_count($requestIp, ':') > 1 ? 'checkIp6' : 'checkIp4'; + + foreach ($ips as $ip) { + if (self::$method($requestIp, $ip)) { + return true; + } + } + + return false; + } + + /** + * Compares two IPv4 addresses. + * In case a subnet is given, it checks if it contains the request IP. + * + * @param string $requestIp IPv4 address to check + * @param string $ip IPv4 address or subnet in CIDR notation + * + * @return bool Whether the request IP matches the IP, or whether the request IP is within the CIDR subnet + */ + public static function checkIp4($requestIp, $ip) + { + $cacheKey = $requestIp.'-'.$ip; + if (isset(self::$checkedIps[$cacheKey])) { + return self::$checkedIps[$cacheKey]; + } + + if (!filter_var($requestIp, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) { + return self::$checkedIps[$cacheKey] = false; + } + + if (false !== strpos($ip, '/')) { + list($address, $netmask) = explode('/', $ip, 2); + + if ('0' === $netmask) { + return self::$checkedIps[$cacheKey] = filter_var($address, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4); + } + + if ($netmask < 0 || $netmask > 32) { + return self::$checkedIps[$cacheKey] = false; + } + } else { + $address = $ip; + $netmask = 32; + } + + if (false === ip2long($address)) { + return self::$checkedIps[$cacheKey] = false; + } + + return self::$checkedIps[$cacheKey] = 0 === substr_compare(sprintf('%032b', ip2long($requestIp)), sprintf('%032b', ip2long($address)), 0, $netmask); + } + + /** + * Compares two IPv6 addresses. + * In case a subnet is given, it checks if it contains the request IP. + * + * @author David Soria Parra + * + * @see https://github.com/dsp/v6tools + * + * @param string $requestIp IPv6 address to check + * @param string $ip IPv6 address or subnet in CIDR notation + * + * @return bool Whether the IP is valid + * + * @throws \RuntimeException When IPV6 support is not enabled + */ + public static function checkIp6($requestIp, $ip) + { + $cacheKey = $requestIp.'-'.$ip; + if (isset(self::$checkedIps[$cacheKey])) { + return self::$checkedIps[$cacheKey]; + } + + if (!((extension_loaded('sockets') && defined('AF_INET6')) || @inet_pton('::1'))) { + throw new \RuntimeException('Unable to check Ipv6. Check that PHP was not compiled with option "disable-ipv6".'); + } + + if (false !== strpos($ip, '/')) { + list($address, $netmask) = explode('/', $ip, 2); + + if ('0' === $netmask) { + return (bool) unpack('n*', @inet_pton($address)); + } + + if ($netmask < 1 || $netmask > 128) { + return self::$checkedIps[$cacheKey] = false; + } + } else { + $address = $ip; + $netmask = 128; + } + + $bytesAddr = unpack('n*', @inet_pton($address)); + $bytesTest = unpack('n*', @inet_pton($requestIp)); + + if (!$bytesAddr || !$bytesTest) { + return self::$checkedIps[$cacheKey] = false; + } + + for ($i = 1, $ceil = ceil($netmask / 16); $i <= $ceil; ++$i) { + $left = $netmask - 16 * ($i - 1); + $left = ($left <= 16) ? $left : 16; + $mask = ~(0xffff >> $left) & 0xffff; + if (($bytesAddr[$i] & $mask) != ($bytesTest[$i] & $mask)) { + return self::$checkedIps[$cacheKey] = false; + } + } + + return self::$checkedIps[$cacheKey] = true; + } +} diff --git a/lib/silex/vendor/symfony/http-foundation/JsonResponse.php b/lib/silex/vendor/symfony/http-foundation/JsonResponse.php new file mode 100644 index 000000000..137ac33c4 --- /dev/null +++ b/lib/silex/vendor/symfony/http-foundation/JsonResponse.php @@ -0,0 +1,220 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation; + +/** + * Response represents an HTTP response in JSON format. + * + * Note that this class does not force the returned JSON content to be an + * 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 + * + * @author Igor Wiedler + */ +class JsonResponse extends Response +{ + protected $data; + protected $callback; + + // Encode <, >, ', &, and " characters in the JSON, making it also safe to be embedded into HTML. + // 15 === JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_AMP | JSON_HEX_QUOT + const DEFAULT_ENCODING_OPTIONS = 15; + + protected $encodingOptions = self::DEFAULT_ENCODING_OPTIONS; + + /** + * @param mixed $data The response data + * @param int $status The response status code + * @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) + { + parent::__construct('', $status, $headers); + + if (null === $data) { + $data = new \ArrayObject(); + } + + $json ? $this->setJson($data) : $this->setData($data); + } + + /** + * Factory method for chainability. + * + * Example: + * + * return JsonResponse::create($data, 200) + * ->setSharedMaxAge(300); + * + * @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()) + { + return new static($data, $status, $headers); + } + + /** + * Make easier the creation of JsonResponse from raw json. + */ + public static function fromJsonString($data = null, $status = 200, $headers = array()) + { + return new static($data, $status, $headers, true); + } + + /** + * Sets the JSONP callback. + * + * @param string|null $callback The JSONP callback or null to use none + * + * @return $this + * + * @throws \InvalidArgumentException When the callback name is not valid + */ + 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://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( + '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)) { + throw new \InvalidArgumentException('The callback name is not valid.'); + } + } + } + + $this->callback = $callback; + + return $this->update(); + } + + /** + * Sets a raw string containing a JSON document to be sent. + * + * @param string $json + * + * @return $this + * + * @throws \InvalidArgumentException + */ + public function setJson($json) + { + $this->data = $json; + + return $this->update(); + } + + /** + * Sets the data to be sent as JSON. + * + * @param mixed $data + * + * @return $this + * + * @throws \InvalidArgumentException + */ + public function setData($data = array()) + { + 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... + $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; + } + } + } + + if (JSON_ERROR_NONE !== json_last_error()) { + throw new \InvalidArgumentException(json_last_error_msg()); + } + + return $this->setJson($data); + } + + /** + * Returns options used while encoding data to JSON. + * + * @return int + */ + public function getEncodingOptions() + { + return $this->encodingOptions; + } + + /** + * Sets options used while encoding data to JSON. + * + * @param int $encodingOptions + * + * @return $this + */ + public function setEncodingOptions($encodingOptions) + { + $this->encodingOptions = (int) $encodingOptions; + + return $this->setData(json_decode($this->data)); + } + + /** + * Updates the content and headers according to the JSON data and callback. + * + * @return $this + */ + protected function update() + { + if (null !== $this->callback) { + // Not using application/javascript for compatibility reasons with older browsers. + $this->headers->set('Content-Type', 'text/javascript'); + + return $this->setContent(sprintf('/**/%s(%s);', $this->callback, $this->data)); + } + + // Only set the header when there is none or when it equals 'text/javascript' (from a previous update with callback) + // in order to not overwrite a custom definition. + if (!$this->headers->has('Content-Type') || 'text/javascript' === $this->headers->get('Content-Type')) { + $this->headers->set('Content-Type', 'application/json'); + } + + return $this->setContent($this->data); + } +} diff --git a/lib/silex/vendor/symfony/http-foundation/LICENSE b/lib/silex/vendor/symfony/http-foundation/LICENSE new file mode 100644 index 000000000..21d7fb9e2 --- /dev/null +++ b/lib/silex/vendor/symfony/http-foundation/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2004-2018 Fabien Potencier + +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/lib/silex/vendor/symfony/http-foundation/ParameterBag.php b/lib/silex/vendor/symfony/http-foundation/ParameterBag.php new file mode 100644 index 000000000..257ef8bce --- /dev/null +++ b/lib/silex/vendor/symfony/http-foundation/ParameterBag.php @@ -0,0 +1,234 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation; + +/** + * ParameterBag is a container for key/value pairs. + * + * @author Fabien Potencier + */ +class ParameterBag implements \IteratorAggregate, \Countable +{ + /** + * Parameter storage. + */ + protected $parameters; + + /** + * @param array $parameters An array of parameters + */ + public function __construct(array $parameters = array()) + { + $this->parameters = $parameters; + } + + /** + * Returns the parameters. + * + * @return array An array of parameters + */ + public function all() + { + return $this->parameters; + } + + /** + * Returns the parameter keys. + * + * @return array An array of parameter keys + */ + public function keys() + { + return array_keys($this->parameters); + } + + /** + * Replaces the current parameters by a new set. + * + * @param array $parameters An array of parameters + */ + public function replace(array $parameters = array()) + { + $this->parameters = $parameters; + } + + /** + * Adds parameters. + * + * @param array $parameters An array of parameters + */ + public function add(array $parameters = array()) + { + $this->parameters = array_replace($this->parameters, $parameters); + } + + /** + * Returns a parameter by name. + * + * @param string $key The key + * @param mixed $default The default value if the parameter key does not exist + * + * @return mixed + */ + public function get($key, $default = null) + { + return array_key_exists($key, $this->parameters) ? $this->parameters[$key] : $default; + } + + /** + * Sets a parameter by name. + * + * @param string $key The key + * @param mixed $value The value + */ + public function set($key, $value) + { + $this->parameters[$key] = $value; + } + + /** + * Returns true if the parameter is defined. + * + * @param string $key The key + * + * @return bool true if the parameter exists, false otherwise + */ + public function has($key) + { + return array_key_exists($key, $this->parameters); + } + + /** + * Removes a parameter. + * + * @param string $key The key + */ + public function remove($key) + { + unset($this->parameters[$key]); + } + + /** + * Returns the alphabetic characters of the parameter value. + * + * @param string $key The parameter key + * @param string $default The default value if the parameter key does not exist + * + * @return string The filtered value + */ + public function getAlpha($key, $default = '') + { + return preg_replace('/[^[:alpha:]]/', '', $this->get($key, $default)); + } + + /** + * Returns the alphabetic characters and digits of the parameter value. + * + * @param string $key The parameter key + * @param string $default The default value if the parameter key does not exist + * + * @return string The filtered value + */ + public function getAlnum($key, $default = '') + { + return preg_replace('/[^[:alnum:]]/', '', $this->get($key, $default)); + } + + /** + * Returns the digits of the parameter value. + * + * @param string $key The parameter key + * @param string $default The default value if the parameter key does not exist + * + * @return string The filtered value + */ + 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)); + } + + /** + * Returns the parameter value converted to integer. + * + * @param string $key The parameter key + * @param int $default The default value if the parameter key does not exist + * + * @return int The filtered value + */ + public function getInt($key, $default = 0) + { + return (int) $this->get($key, $default); + } + + /** + * Returns the parameter value converted to boolean. + * + * @param string $key The parameter key + * @param mixed $default The default value if the parameter key does not exist + * + * @return bool The filtered value + */ + public function getBoolean($key, $default = false) + { + return $this->filter($key, $default, FILTER_VALIDATE_BOOLEAN); + } + + /** + * Filter key. + * + * @param string $key Key + * @param mixed $default Default = null + * @param int $filter FILTER_* constant + * @param mixed $options Filter options + * + * @see http://php.net/manual/en/function.filter-var.php + * + * @return mixed + */ + public function filter($key, $default = null, $filter = FILTER_DEFAULT, $options = array()) + { + $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); + } + + // Add a convenience check for arrays. + if (is_array($value) && !isset($options['flags'])) { + $options['flags'] = FILTER_REQUIRE_ARRAY; + } + + return filter_var($value, $filter, $options); + } + + /** + * Returns an iterator for parameters. + * + * @return \ArrayIterator An \ArrayIterator instance + */ + public function getIterator() + { + return new \ArrayIterator($this->parameters); + } + + /** + * Returns the number of parameters. + * + * @return int The number of parameters + */ + public function count() + { + return count($this->parameters); + } +} diff --git a/lib/silex/vendor/symfony/http-foundation/README.md b/lib/silex/vendor/symfony/http-foundation/README.md new file mode 100644 index 000000000..8907f0b96 --- /dev/null +++ b/lib/silex/vendor/symfony/http-foundation/README.md @@ -0,0 +1,14 @@ +HttpFoundation Component +======================== + +The HttpFoundation component defines an object-oriented layer for the HTTP +specification. + +Resources +--------- + + * [Documentation](https://symfony.com/doc/current/components/http_foundation/index.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/lib/silex/vendor/symfony/http-foundation/RedirectResponse.php b/lib/silex/vendor/symfony/http-foundation/RedirectResponse.php new file mode 100644 index 000000000..01681dcdf --- /dev/null +++ b/lib/silex/vendor/symfony/http-foundation/RedirectResponse.php @@ -0,0 +1,109 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation; + +/** + * RedirectResponse represents an HTTP response doing a redirect. + * + * @author Fabien Potencier + */ +class RedirectResponse extends Response +{ + protected $targetUrl; + + /** + * Creates a redirect response so that it conforms to the rules defined for a redirect status code. + * + * @param string $url The URL to redirect to. The URL should be a full URL, with schema etc., + * but practically every browser redirects on paths only as well + * @param int $status The status code (302 by default) + * @param array $headers The headers (Location is always set to the given URL) + * + * @throws \InvalidArgumentException + * + * @see http://tools.ietf.org/html/rfc2616#section-10.3 + */ + public function __construct($url, $status = 302, $headers = array()) + { + parent::__construct('', $status, $headers); + + $this->setTargetUrl($url); + + if (!$this->isRedirect()) { + throw new \InvalidArgumentException(sprintf('The HTTP status code is not a redirect ("%s" given).', $status)); + } + + if (301 == $status && !array_key_exists('cache-control', $headers)) { + $this->headers->remove('cache-control'); + } + } + + /** + * Factory method for chainability. + * + * @param string $url The url to redirect to + * @param int $status The response status code + * @param array $headers An array of response headers + * + * @return static + */ + public static function create($url = '', $status = 302, $headers = array()) + { + return new static($url, $status, $headers); + } + + /** + * Returns the target URL. + * + * @return string target URL + */ + public function getTargetUrl() + { + return $this->targetUrl; + } + + /** + * Sets the redirect target of this response. + * + * @param string $url The URL to redirect to + * + * @return $this + * + * @throws \InvalidArgumentException + */ + public function setTargetUrl($url) + { + if (empty($url)) { + throw new \InvalidArgumentException('Cannot redirect to an empty URL.'); + } + + $this->targetUrl = $url; + + $this->setContent( + sprintf(' + + + + + + Redirecting to %1$s + + + Redirecting to %1$s. + +', htmlspecialchars($url, ENT_QUOTES, 'UTF-8'))); + + $this->headers->set('Location', $url); + + return $this; + } +} diff --git a/lib/silex/vendor/symfony/http-foundation/Request.php b/lib/silex/vendor/symfony/http-foundation/Request.php new file mode 100644 index 000000000..be55069e4 --- /dev/null +++ b/lib/silex/vendor/symfony/http-foundation/Request.php @@ -0,0 +1,2154 @@ + + * + * 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\HttpFoundation\Exception\ConflictingHeadersException; +use Symfony\Component\HttpFoundation\Exception\SuspiciousOperationException; +use Symfony\Component\HttpFoundation\Session\SessionInterface; + +/** + * Request represents an HTTP request. + * + * The methods dealing with URL accept / return a raw path (% encoded): + * * getBasePath + * * getBaseUrl + * * getPathInfo + * * getRequestUri + * * getUri + * * getUriForPath + * + * @author Fabien Potencier + */ +class Request +{ + const HEADER_FORWARDED = 0b00001; // When using RFC 7239 + const HEADER_X_FORWARDED_FOR = 0b00010; + const HEADER_X_FORWARDED_HOST = 0b00100; + const HEADER_X_FORWARDED_PROTO = 0b01000; + const HEADER_X_FORWARDED_PORT = 0b10000; + 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'; + const METHOD_PUT = 'PUT'; + const METHOD_PATCH = 'PATCH'; + const METHOD_DELETE = 'DELETE'; + const METHOD_PURGE = 'PURGE'; + const METHOD_OPTIONS = 'OPTIONS'; + const METHOD_TRACE = 'TRACE'; + const METHOD_CONNECT = 'CONNECT'; + + /** + * @var string[] + */ + protected static $trustedProxies = array(); + + /** + * @var string[] + */ + protected static $trustedHostPatterns = array(); + + /** + * @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 $httpMethodParameterOverride = false; + + /** + * Custom parameters. + * + * @var \Symfony\Component\HttpFoundation\ParameterBag + */ + public $attributes; + + /** + * Request body parameters ($_POST). + * + * @var \Symfony\Component\HttpFoundation\ParameterBag + */ + public $request; + + /** + * Query string parameters ($_GET). + * + * @var \Symfony\Component\HttpFoundation\ParameterBag + */ + public $query; + + /** + * Server and execution environment parameters ($_SERVER). + * + * @var \Symfony\Component\HttpFoundation\ServerBag + */ + public $server; + + /** + * Uploaded files ($_FILES). + * + * @var \Symfony\Component\HttpFoundation\FileBag + */ + public $files; + + /** + * Cookies ($_COOKIE). + * + * @var \Symfony\Component\HttpFoundation\ParameterBag + */ + public $cookies; + + /** + * Headers (taken from the $_SERVER). + * + * @var \Symfony\Component\HttpFoundation\HeaderBag + */ + public $headers; + + /** + * @var string|resource + */ + protected $content; + + /** + * @var array + */ + protected $languages; + + /** + * @var array + */ + protected $charsets; + + /** + * @var array + */ + protected $encodings; + + /** + * @var array + */ + protected $acceptableContentTypes; + + /** + * @var string + */ + protected $pathInfo; + + /** + * @var string + */ + protected $requestUri; + + /** + * @var string + */ + protected $baseUrl; + + /** + * @var string + */ + protected $basePath; + + /** + * @var string + */ + protected $method; + + /** + * @var string + */ + protected $format; + + /** + * @var \Symfony\Component\HttpFoundation\Session\SessionInterface + */ + protected $session; + + /** + * @var string + */ + protected $locale; + + /** + * @var string + */ + protected $defaultLocale = 'en'; + + /** + * @var array + */ + protected static $formats; + + protected static $requestFactory; + + 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( + self::HEADER_X_FORWARDED_FOR => 'for', + self::HEADER_X_FORWARDED_HOST => 'host', + self::HEADER_X_FORWARDED_PROTO => 'proto', + self::HEADER_X_FORWARDED_PORT => 'host', + ); + + /** + * @param array $query The GET parameters + * @param array $request The POST parameters + * @param array $attributes The request attributes (parameters parsed from the PATH_INFO, ...) + * @param array $cookies The COOKIE parameters + * @param array $files The FILES parameters + * @param array $server The SERVER parameters + * @param string|resource $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) + { + $this->initialize($query, $request, $attributes, $cookies, $files, $server, $content); + } + + /** + * Sets the parameters for this request. + * + * This method also re-initializes all properties. + * + * @param array $query The GET parameters + * @param array $request The POST parameters + * @param array $attributes The request attributes (parameters parsed from the PATH_INFO, ...) + * @param array $cookies The COOKIE parameters + * @param array $files The FILES parameters + * @param array $server The SERVER parameters + * @param string|resource $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) + { + $this->request = new ParameterBag($request); + $this->query = new ParameterBag($query); + $this->attributes = new ParameterBag($attributes); + $this->cookies = new ParameterBag($cookies); + $this->files = new FileBag($files); + $this->server = new ServerBag($server); + $this->headers = new HeaderBag($this->server->getHeaders()); + + $this->content = $content; + $this->languages = null; + $this->charsets = null; + $this->encodings = null; + $this->acceptableContentTypes = null; + $this->pathInfo = null; + $this->requestUri = null; + $this->baseUrl = null; + $this->basePath = null; + $this->method = null; + $this->format = null; + } + + /** + * Creates a new request with values from PHP's super globals. + * + * @return static + */ + 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); + + 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')) + ) { + parse_str($request->getContent(), $data); + $request->request = new ParameterBag($data); + } + + return $request; + } + + /** + * Creates a Request based on a given URI and configuration. + * + * The information contained in the URI always take precedence + * over the other information (server and parameters). + * + * @param string $uri The URI + * @param string $method The HTTP method + * @param array $parameters The query (GET) or request (POST) parameters + * @param array $cookies The request cookies ($_COOKIE) + * @param array $files The request files ($_FILES) + * @param array $server The server parameters ($_SERVER) + * @param string|resource $content The raw body data + * + * @return static + */ + public static function create($uri, $method = 'GET', $parameters = array(), $cookies = array(), $files = array(), $server = array(), $content = null) + { + $server = array_replace(array( + 'SERVER_NAME' => 'localhost', + 'SERVER_PORT' => 80, + 'HTTP_HOST' => 'localhost', + 'HTTP_USER_AGENT' => 'Symfony/3.X', + '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', + 'REMOTE_ADDR' => '127.0.0.1', + 'SCRIPT_NAME' => '', + 'SCRIPT_FILENAME' => '', + 'SERVER_PROTOCOL' => 'HTTP/1.1', + 'REQUEST_TIME' => time(), + ), $server); + + $server['PATH_INFO'] = ''; + $server['REQUEST_METHOD'] = strtoupper($method); + + $components = parse_url($uri); + if (isset($components['host'])) { + $server['SERVER_NAME'] = $components['host']; + $server['HTTP_HOST'] = $components['host']; + } + + if (isset($components['scheme'])) { + if ('https' === $components['scheme']) { + $server['HTTPS'] = 'on'; + $server['SERVER_PORT'] = 443; + } else { + unset($server['HTTPS']); + $server['SERVER_PORT'] = 80; + } + } + + if (isset($components['port'])) { + $server['SERVER_PORT'] = $components['port']; + $server['HTTP_HOST'] = $server['HTTP_HOST'].':'.$components['port']; + } + + if (isset($components['user'])) { + $server['PHP_AUTH_USER'] = $components['user']; + } + + if (isset($components['pass'])) { + $server['PHP_AUTH_PW'] = $components['pass']; + } + + if (!isset($components['path'])) { + $components['path'] = '/'; + } + + switch (strtoupper($method)) { + case 'POST': + case 'PUT': + case 'DELETE': + if (!isset($server['CONTENT_TYPE'])) { + $server['CONTENT_TYPE'] = 'application/x-www-form-urlencoded'; + } + // no break + case 'PATCH': + $request = $parameters; + $query = array(); + break; + default: + $request = array(); + $query = $parameters; + break; + } + + $queryString = ''; + if (isset($components['query'])) { + parse_str(html_entity_decode($components['query']), $qs); + + if ($query) { + $query = array_replace($qs, $query); + $queryString = http_build_query($query, '', '&'); + } else { + $query = $qs; + $queryString = $components['query']; + } + } elseif ($query) { + $queryString = http_build_query($query, '', '&'); + } + + $server['REQUEST_URI'] = $components['path'].('' !== $queryString ? '?'.$queryString : ''); + $server['QUERY_STRING'] = $queryString; + + return self::createRequestFromFactory($query, $request, array(), $cookies, $files, $server, $content); + } + + /** + * Sets a callable able to create a Request instance. + * + * This is mainly useful when you need to override the Request class + * to keep BC with an existing system. It should not be used for any + * other purpose. + * + * @param callable|null $callable A PHP callable + */ + public static function setFactory($callable) + { + self::$requestFactory = $callable; + } + + /** + * Clones a request and overrides some of its parameters. + * + * @param array $query The GET parameters + * @param array $request The POST parameters + * @param array $attributes The request attributes (parameters parsed from the PATH_INFO, ...) + * @param array $cookies The COOKIE parameters + * @param array $files The FILES parameters + * @param array $server The SERVER parameters + * + * @return static + */ + public function duplicate(array $query = null, array $request = null, array $attributes = null, array $cookies = null, array $files = null, array $server = null) + { + $dup = clone $this; + if (null !== $query) { + $dup->query = new ParameterBag($query); + } + if (null !== $request) { + $dup->request = new ParameterBag($request); + } + if (null !== $attributes) { + $dup->attributes = new ParameterBag($attributes); + } + if (null !== $cookies) { + $dup->cookies = new ParameterBag($cookies); + } + if (null !== $files) { + $dup->files = new FileBag($files); + } + if (null !== $server) { + $dup->server = new ServerBag($server); + $dup->headers = new HeaderBag($dup->server->getHeaders()); + } + $dup->languages = null; + $dup->charsets = null; + $dup->encodings = null; + $dup->acceptableContentTypes = null; + $dup->pathInfo = null; + $dup->requestUri = null; + $dup->baseUrl = null; + $dup->basePath = null; + $dup->method = null; + $dup->format = null; + + if (!$dup->get('_format') && $this->get('_format')) { + $dup->attributes->set('_format', $this->get('_format')); + } + + if (!$dup->getRequestFormat(null)) { + $dup->setRequestFormat($this->getRequestFormat(null)); + } + + return $dup; + } + + /** + * Clones the current request. + * + * Note that the session is not cloned as duplicated requests + * are most of the time sub-requests of the main one. + */ + public function __clone() + { + $this->query = clone $this->query; + $this->request = clone $this->request; + $this->attributes = clone $this->attributes; + $this->cookies = clone $this->cookies; + $this->files = clone $this->files; + $this->server = clone $this->server; + $this->headers = clone $this->headers; + } + + /** + * Returns the request as a string. + * + * @return string The request + */ + public function __toString() + { + try { + $content = $this->getContent(); + } catch (\LogicException $e) { + return trigger_error($e, E_USER_ERROR); + } + + $cookieHeader = ''; + $cookies = array(); + + foreach ($this->cookies as $k => $v) { + $cookies[] = $k.'='.$v; + } + + if (!empty($cookies)) { + $cookieHeader = 'Cookie: '.implode('; ', $cookies)."\r\n"; + } + + return + sprintf('%s %s %s', $this->getMethod(), $this->getRequestUri(), $this->server->get('SERVER_PROTOCOL'))."\r\n". + $this->headers. + $cookieHeader."\r\n". + $content; + } + + /** + * Overrides the PHP global variables according to this request instance. + * + * It overrides $_GET, $_POST, $_REQUEST, $_SERVER, $_COOKIE. + * $_FILES is never overridden, see rfc1867 + */ + public function overrideGlobals() + { + $this->server->set('QUERY_STRING', static::normalizeQueryString(http_build_query($this->query->all(), null, '&'))); + + $_GET = $this->query->all(); + $_POST = $this->request->all(); + $_SERVER = $this->server->all(); + $_COOKIE = $this->cookies->all(); + + foreach ($this->headers->all() as $key => $value) { + $key = strtoupper(str_replace('-', '_', $key)); + if (in_array($key, array('CONTENT_TYPE', 'CONTENT_LENGTH'))) { + $_SERVER[$key] = implode(', ', $value); + } else { + $_SERVER['HTTP_'.$key] = implode(', ', $value); + } + } + + $request = array('g' => $_GET, 'p' => $_POST, 'c' => $_COOKIE); + + $requestOrder = ini_get('request_order') ?: ini_get('variables_order'); + $requestOrder = preg_replace('#[^cgp]#', '', strtolower($requestOrder)) ?: 'gp'; + + $_REQUEST = array(); + foreach (str_split($requestOrder) as $order) { + $_REQUEST = array_merge($_REQUEST, $request[$order]); + } + } + + /** + * Sets a list of trusted proxies. + * + * You should only list the reverse proxies that you manage directly. + * + * @param array $proxies A list of trusted proxies + * @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*/) + { + 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); + + foreach (self::$trustedHeaderNames as $header => $name) { + self::$trustedHeaders[$header] = $header & $trustedHeaderSet ? $name : null; + } + self::$trustedHeaderSet = $trustedHeaderSet; + } + + /** + * Gets the list of trusted proxies. + * + * @return array An array of trusted proxies + */ + public static function getTrustedProxies() + { + return self::$trustedProxies; + } + + /** + * Gets the set of trusted headers from trusted proxies. + * + * @return int A bit field of Request::HEADER_* that defines which headers are trusted from your proxies + */ + public static function getTrustedHeaderSet() + { + return self::$trustedHeaderSet; + } + + /** + * Sets a list of trusted host patterns. + * + * You should only list the hosts you manage using regexs. + * + * @param array $hostPatterns A list of trusted host patterns + */ + public static function setTrustedHosts(array $hostPatterns) + { + self::$trustedHostPatterns = array_map(function ($hostPattern) { + return sprintf('#%s#i', $hostPattern); + }, $hostPatterns); + // we need to reset trusted hosts on trusted host patterns change + self::$trustedHosts = array(); + } + + /** + * Gets the list of trusted host patterns. + * + * @return array An array of trusted host patterns + */ + 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. + * + * It builds a normalized query string, where keys/value pairs are alphabetized, + * have consistent escaping and unneeded delimiters are removed. + * + * @param string $qs Query string + * + * @return string A normalized query string for the Request + */ + public static function normalizeQueryString($qs) + { + if ('' == $qs) { + return ''; + } + + $parts = array(); + $order = array(); + + 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); + } + + /** + * Enables support for the _method request parameter to determine the intended HTTP method. + * + * Be warned that enabling this feature might lead to CSRF issues in your code. + * Check that you are using CSRF tokens when required. + * If the HTTP method parameter override is enabled, an html-form with method "POST" can be altered + * and used to send a "PUT" or "DELETE" request via the _method request parameter. + * If these methods are not protected against CSRF, this presents a possible vulnerability. + * + * The HTTP method can only be overridden when the real HTTP method is POST. + */ + public static function enableHttpMethodParameterOverride() + { + self::$httpMethodParameterOverride = true; + } + + /** + * Checks whether support for the _method request parameter is enabled. + * + * @return bool True when the _method request parameter is enabled, false otherwise + */ + public static function getHttpMethodParameterOverride() + { + return self::$httpMethodParameterOverride; + } + + /** + * Gets a "parameter" value from any bag. + * + * This method is mainly useful for libraries that want to provide some flexibility. If you don't need the + * flexibility in controllers, it is better to explicitly get request parameters from the appropriate + * public property instead (attributes, query, request). + * + * Order of precedence: PATH (routing placeholders or custom attributes), GET, BODY + * + * @param string $key The key + * @param mixed $default The default value if the parameter key does not exist + * + * @return mixed + */ + public function get($key, $default = null) + { + if ($this !== $result = $this->attributes->get($key, $this)) { + return $result; + } + + if ($this !== $result = $this->query->get($key, $this)) { + return $result; + } + + if ($this !== $result = $this->request->get($key, $this)) { + return $result; + } + + return $default; + } + + /** + * Gets the Session. + * + * @return SessionInterface|null The session + */ + public function getSession() + { + return $this->session; + } + + /** + * Whether the request contains a Session which was started in one of the + * previous requests. + * + * @return bool + */ + 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()); + } + + /** + * Whether the request contains a Session object. + * + * This method does not give any information about the state of the session object, + * like whether the session is started or not. It is just a way to check if this Request + * is associated with a Session instance. + * + * @return bool true when the Request contains a Session object, false otherwise + */ + public function hasSession() + { + return null !== $this->session; + } + + /** + * Sets the Session. + * + * @param SessionInterface $session The Session + */ + public function setSession(SessionInterface $session) + { + $this->session = $session; + } + + /** + * Returns the client IP addresses. + * + * In the returned array the most trusted IP address is first, and the + * least trusted one last. The "real" client IP address is the last one, + * but this is also the least trusted one. Trusted proxies are stripped. + * + * Use this method carefully; you should use getClientIp() instead. + * + * @return array The client IP addresses + * + * @see getClientIp() + */ + public function getClientIps() + { + $ip = $this->server->get('REMOTE_ADDR'); + + if (!$this->isFromTrustedProxy()) { + return array($ip); + } + + return $this->getTrustedValues(self::HEADER_CLIENT_IP, $ip) ?: array($ip); + } + + /** + * Returns the client IP address. + * + * This method can read the client IP address from the "X-Forwarded-For" header + * when trusted proxies were set via "setTrustedProxies()". The "X-Forwarded-For" + * header value is a comma+space separated list of IP addresses, the left-most + * being the original client, and each successive proxy that passed the request + * adding the IP address where it received the request from. + * + * If your reverse proxy uses a different header name than "X-Forwarded-For", + * ("Client-Ip" for instance), configure it via the $trustedHeaderSet + * argument of the Request::setTrustedProxies() method instead. + * + * @return string|null The client IP address + * + * @see getClientIps() + * @see http://en.wikipedia.org/wiki/X-Forwarded-For + */ + public function getClientIp() + { + $ipAddresses = $this->getClientIps(); + + return $ipAddresses[0]; + } + + /** + * Returns current script name. + * + * @return string + */ + public function getScriptName() + { + return $this->server->get('SCRIPT_NAME', $this->server->get('ORIG_SCRIPT_NAME', '')); + } + + /** + * Returns the path being requested relative to the executed script. + * + * The path info always starts with a /. + * + * Suppose this request is instantiated from /mysite on localhost: + * + * * http://localhost/mysite returns an empty string + * * http://localhost/mysite/about returns '/about' + * * http://localhost/mysite/enco%20ded returns '/enco%20ded' + * * http://localhost/mysite/about?var=1 returns '/about' + * + * @return string The raw path (i.e. not urldecoded) + */ + public function getPathInfo() + { + if (null === $this->pathInfo) { + $this->pathInfo = $this->preparePathInfo(); + } + + return $this->pathInfo; + } + + /** + * Returns the root path from which this request is executed. + * + * Suppose that an index.php file instantiates this request object: + * + * * http://localhost/index.php returns an empty string + * * http://localhost/index.php/page returns an empty string + * * http://localhost/web/index.php returns '/web' + * * http://localhost/we%20b/index.php returns '/we%20b' + * + * @return string The raw path (i.e. not urldecoded) + */ + public function getBasePath() + { + if (null === $this->basePath) { + $this->basePath = $this->prepareBasePath(); + } + + return $this->basePath; + } + + /** + * Returns the root URL from which this request is executed. + * + * The base URL never ends with a /. + * + * This is similar to getBasePath(), except that it also includes the + * script filename (e.g. index.php) if one exists. + * + * @return string The raw URL (i.e. not urldecoded) + */ + public function getBaseUrl() + { + if (null === $this->baseUrl) { + $this->baseUrl = $this->prepareBaseUrl(); + } + + return $this->baseUrl; + } + + /** + * Gets the request's scheme. + * + * @return string + */ + public function getScheme() + { + return $this->isSecure() ? 'https' : 'http'; + } + + /** + * Returns the port on which the request is made. + * + * This method can read the client port from the "X-Forwarded-Port" header + * when trusted proxies were set via "setTrustedProxies()". + * + * 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)) { + $host = $host[0]; + } elseif ($this->isFromTrustedProxy() && $host = $this->getTrustedValues(self::HEADER_CLIENT_HOST)) { + $host = $host[0]; + } elseif (!$host = $this->headers->get('HOST')) { + return $this->server->get('SERVER_PORT'); + } + + if ('[' === $host[0]) { + $pos = strpos($host, ':', strrpos($host, ']')); + } else { + $pos = strrpos($host, ':'); + } + + if (false !== $pos) { + return (int) substr($host, $pos + 1); + } + + return 'https' === $this->getScheme() ? 443 : 80; + } + + /** + * Returns the user. + * + * @return string|null + */ + public function getUser() + { + return $this->headers->get('PHP_AUTH_USER'); + } + + /** + * Returns the password. + * + * @return string|null + */ + public function getPassword() + { + return $this->headers->get('PHP_AUTH_PW'); + } + + /** + * Gets the user info. + * + * @return string A user name and, optionally, scheme-specific information about how to gain authorization to access the server + */ + public function getUserInfo() + { + $userinfo = $this->getUser(); + + $pass = $this->getPassword(); + if ('' != $pass) { + $userinfo .= ":$pass"; + } + + return $userinfo; + } + + /** + * Returns the HTTP host being requested. + * + * The port name will be appended to the host if it's non-standard. + * + * @return string + */ + public function getHttpHost() + { + $scheme = $this->getScheme(); + $port = $this->getPort(); + + if (('http' == $scheme && 80 == $port) || ('https' == $scheme && 443 == $port)) { + return $this->getHost(); + } + + return $this->getHost().':'.$port; + } + + /** + * Returns the requested URI (path and query string). + * + * @return string The raw URI (i.e. not URI decoded) + */ + public function getRequestUri() + { + if (null === $this->requestUri) { + $this->requestUri = $this->prepareRequestUri(); + } + + return $this->requestUri; + } + + /** + * Gets the scheme and HTTP host. + * + * If the URL was called with basic authentication, the user + * and the password are not added to the generated string. + * + * @return string The scheme and HTTP host + */ + public function getSchemeAndHttpHost() + { + return $this->getScheme().'://'.$this->getHttpHost(); + } + + /** + * Generates a normalized URI (URL) for the Request. + * + * @return string A normalized URI (URL) for the Request + * + * @see getQueryString() + */ + public function getUri() + { + if (null !== $qs = $this->getQueryString()) { + $qs = '?'.$qs; + } + + return $this->getSchemeAndHttpHost().$this->getBaseUrl().$this->getPathInfo().$qs; + } + + /** + * Generates a normalized URI for the given path. + * + * @param string $path A path to use instead of the current one + * + * @return string The normalized URI for the path + */ + public function getUriForPath($path) + { + return $this->getSchemeAndHttpHost().$this->getBaseUrl().$path; + } + + /** + * Returns the path as relative reference from the current Request path. + * + * Only the URIs path component (no schema, host etc.) is relevant and must be given. + * Both paths must be absolute and not contain relative parts. + * Relative URLs from one resource to another are useful when generating self-contained downloadable document archives. + * Furthermore, they can be used to reduce the link size in documents. + * + * Example target paths, given a base path of "/a/b/c/d": + * - "/a/b/c/d" -> "" + * - "/a/b/c/" -> "./" + * - "/a/b/" -> "../" + * - "/a/b/c/other" -> "other" + * - "/a/x/y" -> "../../x/y" + * + * @param string $path The target path + * + * @return string The relative target path + */ + public function getRelativeUriForPath($path) + { + // be sure that we are dealing with an absolute path + if (!isset($path[0]) || '/' !== $path[0]) { + return $path; + } + + if ($path === $basePath = $this->getPathInfo()) { + return ''; + } + + $sourceDirs = explode('/', isset($basePath[0]) && '/' === $basePath[0] ? substr($basePath, 1) : $basePath); + $targetDirs = explode('/', isset($path[0]) && '/' === $path[0] ? substr($path, 1) : $path); + array_pop($sourceDirs); + $targetFile = array_pop($targetDirs); + + foreach ($sourceDirs as $i => $dir) { + if (isset($targetDirs[$i]) && $dir === $targetDirs[$i]) { + unset($sourceDirs[$i], $targetDirs[$i]); + } else { + break; + } + } + + $targetDirs[] = $targetFile; + $path = str_repeat('../', count($sourceDirs)).implode('/', $targetDirs); + + // 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). + return !isset($path[0]) || '/' === $path[0] + || false !== ($colonPos = strpos($path, ':')) && ($colonPos < ($slashPos = strpos($path, '/')) || false === $slashPos) + ? "./$path" : $path; + } + + /** + * Generates the normalized query string for the Request. + * + * It builds a normalized query string, where keys/value pairs are alphabetized + * and have consistent escaping. + * + * @return string|null A normalized query string for the Request + */ + public function getQueryString() + { + $qs = static::normalizeQueryString($this->server->get('QUERY_STRING')); + + return '' === $qs ? null : $qs; + } + + /** + * Checks whether the request is secure or not. + * + * This method can read the client protocol from the "X-Forwarded-Proto" header + * when trusted proxies were set via "setTrustedProxies()". + * + * 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); + } + + $https = $this->server->get('HTTPS'); + + return !empty($https) && 'off' !== strtolower($https); + } + + /** + * Returns the host name. + * + * This method can read the client host name from the "X-Forwarded-Host" header + * when trusted proxies were set via "setTrustedProxies()". + * + * 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)) { + $host = $host[0]; + } elseif (!$host = $this->headers->get('HOST')) { + if (!$host = $this->server->get('SERVER_NAME')) { + $host = $this->server->get('SERVER_ADDR', ''); + } + } + + // trim and remove port number from host + // host is lowercase as per RFC 952/2181 + $host = strtolower(preg_replace('/:\d+$/', '', trim($host))); + + // as the host can come from the user (HTTP_HOST and depending on the configuration, SERVER_NAME too can come from the user) + // check that it does not contain forbidden characters (see RFC 952 and RFC 2181) + // use preg_replace() instead of preg_match() to prevent DoS attacks with long host names + if ($host && '' !== preg_replace('/(?:^\[)?[a-zA-Z0-9-:\]_]+\.?/', '', $host)) { + if (!$this->isHostValid) { + return ''; + } + $this->isHostValid = false; + + throw new SuspiciousOperationException(sprintf('Invalid Host "%s".', $host)); + } + + if (count(self::$trustedHostPatterns) > 0) { + // to avoid host header injection attacks, you should provide a list of trusted host patterns + + if (in_array($host, self::$trustedHosts)) { + return $host; + } + + foreach (self::$trustedHostPatterns as $pattern) { + if (preg_match($pattern, $host)) { + self::$trustedHosts[] = $host; + + return $host; + } + } + + if (!$this->isHostValid) { + return ''; + } + $this->isHostValid = false; + + throw new SuspiciousOperationException(sprintf('Untrusted Host "%s".', $host)); + } + + return $host; + } + + /** + * Sets the request method. + * + * @param string $method + */ + public function setMethod($method) + { + $this->method = null; + $this->server->set('REQUEST_METHOD', $method); + } + + /** + * Gets the request "intended" method. + * + * If the X-HTTP-Method-Override header is set, and if the method is a POST, + * then it is used to determine the "real" intended HTTP method. + * + * The _method request parameter can also be used to determine the HTTP method, + * but only if enableHttpMethodParameterOverride() has been called. + * + * The method is always an uppercased string. + * + * @return string The request method + * + * @see getRealMethod() + */ + public function getMethod() + { + if (null === $this->method) { + $this->method = strtoupper($this->server->get('REQUEST_METHOD', 'GET')); + + if ('POST' === $this->method) { + if ($method = $this->headers->get('X-HTTP-METHOD-OVERRIDE')) { + $this->method = strtoupper($method); + } elseif (self::$httpMethodParameterOverride) { + $this->method = strtoupper($this->request->get('_method', $this->query->get('_method', 'POST'))); + } + } + } + + return $this->method; + } + + /** + * Gets the "real" request method. + * + * @return string The request method + * + * @see getMethod() + */ + public function getRealMethod() + { + return strtoupper($this->server->get('REQUEST_METHOD', 'GET')); + } + + /** + * Gets the mime type associated with the format. + * + * @param string $format The format + * + * @return string The associated mime type (null if not found) + */ + public function getMimeType($format) + { + if (null === static::$formats) { + static::initializeFormats(); + } + + return isset(static::$formats[$format]) ? static::$formats[$format][0] : null; + } + + /** + * Gets the mime types associated with the format. + * + * @param string $format The format + * + * @return array The associated mime types + */ + public static function getMimeTypes($format) + { + if (null === static::$formats) { + static::initializeFormats(); + } + + return isset(static::$formats[$format]) ? static::$formats[$format] : array(); + } + + /** + * Gets the format associated with the mime type. + * + * @param string $mimeType The associated mime type + * + * @return string|null The format (null if not found) + */ + public function getFormat($mimeType) + { + $canonicalMimeType = null; + if (false !== $pos = strpos($mimeType, ';')) { + $canonicalMimeType = substr($mimeType, 0, $pos); + } + + if (null === static::$formats) { + static::initializeFormats(); + } + + foreach (static::$formats as $format => $mimeTypes) { + if (in_array($mimeType, (array) $mimeTypes)) { + return $format; + } + if (null !== $canonicalMimeType && in_array($canonicalMimeType, (array) $mimeTypes)) { + return $format; + } + } + } + + /** + * Associates a format with mime types. + * + * @param string $format The format + * @param string|array $mimeTypes The associated mime types (the preferred one must be the first as it will be used as the content type) + */ + public function setFormat($format, $mimeTypes) + { + if (null === static::$formats) { + static::initializeFormats(); + } + + static::$formats[$format] = is_array($mimeTypes) ? $mimeTypes : array($mimeTypes); + } + + /** + * Gets the request format. + * + * Here is the process to determine the format: + * + * * format defined by the user (with setRequestFormat()) + * * _format request attribute + * * $default + * + * @param string $default The default format + * + * @return string The request format + */ + public function getRequestFormat($default = 'html') + { + if (null === $this->format) { + $this->format = $this->attributes->get('_format'); + } + + return null === $this->format ? $default : $this->format; + } + + /** + * Sets the request format. + * + * @param string $format The request format + */ + public function setRequestFormat($format) + { + $this->format = $format; + } + + /** + * Gets the format associated with the request. + * + * @return string|null The format (null if no content type is present) + */ + public function getContentType() + { + return $this->getFormat($this->headers->get('CONTENT_TYPE')); + } + + /** + * Sets the default locale. + * + * @param string $locale + */ + public function setDefaultLocale($locale) + { + $this->defaultLocale = $locale; + + if (null === $this->locale) { + $this->setPhpDefaultLocale($locale); + } + } + + /** + * Get the default locale. + * + * @return string + */ + public function getDefaultLocale() + { + return $this->defaultLocale; + } + + /** + * Sets the locale. + * + * @param string $locale + */ + public function setLocale($locale) + { + $this->setPhpDefaultLocale($this->locale = $locale); + } + + /** + * Get the locale. + * + * @return string + */ + public function getLocale() + { + return null === $this->locale ? $this->defaultLocale : $this->locale; + } + + /** + * Checks if the request method is of specified type. + * + * @param string $method Uppercase request method (GET, POST etc) + * + * @return bool + */ + public function isMethod($method) + { + return $this->getMethod() === strtoupper($method); + } + + /** + * Checks whether or not the method is safe. + * + * @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 */) + { + 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')); + } + + return in_array($this->getMethod(), array('GET', 'HEAD', 'OPTIONS', 'TRACE')); + } + + /** + * Checks whether or not the method is idempotent. + * + * @return bool + */ + public function isMethodIdempotent() + { + return in_array($this->getMethod(), array('HEAD', 'GET', 'PUT', 'DELETE', 'TRACE', 'OPTIONS', 'PURGE')); + } + + /** + * Checks whether the method is cacheable or not. + * + * @see https://tools.ietf.org/html/rfc7231#section-4.2.3 + * + * @return bool + */ + public function isMethodCacheable() + { + return in_array($this->getMethod(), array('GET', 'HEAD')); + } + + /** + * Returns the protocol version. + * + * If the application is behind a proxy, the protocol version used in the + * requests between the client and the proxy and between the proxy and the + * server might be different. This returns the former (from the "Via" header) + * if the proxy is trusted (see "setTrustedProxies()"), otherwise it returns + * the latter (from the "SERVER_PROTOCOL" server parameter). + * + * @return string + */ + public function getProtocolVersion() + { + if ($this->isFromTrustedProxy()) { + preg_match('~^(HTTP/)?([1-9]\.[0-9]) ~', $this->headers->get('Via'), $matches); + + if ($matches) { + return 'HTTP/'.$matches[2]; + } + } + + return $this->server->get('SERVER_PROTOCOL'); + } + + /** + * Returns the request body content. + * + * @param bool $asResource If true, a resource will be returned + * + * @return string|resource The request body content or a resource to read the body stream + * + * @throws \LogicException + */ + 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) { + rewind($this->content); + + return $this->content; + } + + // Content passed in parameter (test) + if (is_string($this->content)) { + $resource = fopen('php://temp', 'r+'); + fwrite($resource, $this->content); + rewind($resource); + + return $resource; + } + + $this->content = false; + + return fopen('php://input', 'rb'); + } + + if ($currentContentIsResource) { + rewind($this->content); + + return stream_get_contents($this->content); + } + + if (null === $this->content || false === $this->content) { + $this->content = file_get_contents('php://input'); + } + + return $this->content; + } + + /** + * Gets the Etags. + * + * @return array The entity tags + */ + public function getETags() + { + return preg_split('/\s*,\s*/', $this->headers->get('if_none_match'), null, PREG_SPLIT_NO_EMPTY); + } + + /** + * @return bool + */ + public function isNoCache() + { + return $this->headers->hasCacheControlDirective('no-cache') || 'no-cache' == $this->headers->get('Pragma'); + } + + /** + * Returns the preferred language. + * + * @param array $locales An array of ordered available locales + * + * @return string|null The preferred locale + */ + public function getPreferredLanguage(array $locales = null) + { + $preferredLanguages = $this->getLanguages(); + + if (empty($locales)) { + return isset($preferredLanguages[0]) ? $preferredLanguages[0] : null; + } + + if (!$preferredLanguages) { + return $locales[0]; + } + + $extendedPreferredLanguages = array(); + foreach ($preferredLanguages as $language) { + $extendedPreferredLanguages[] = $language; + if (false !== $position = strpos($language, '_')) { + $superLanguage = substr($language, 0, $position); + if (!in_array($superLanguage, $preferredLanguages)) { + $extendedPreferredLanguages[] = $superLanguage; + } + } + } + + $preferredLanguages = array_values(array_intersect($extendedPreferredLanguages, $locales)); + + return isset($preferredLanguages[0]) ? $preferredLanguages[0] : $locales[0]; + } + + /** + * Gets a list of languages acceptable by the client browser. + * + * @return array Languages ordered in the user browser preferences + */ + public function getLanguages() + { + if (null !== $this->languages) { + return $this->languages; + } + + $languages = AcceptHeader::fromString($this->headers->get('Accept-Language'))->all(); + $this->languages = array(); + foreach ($languages as $lang => $acceptHeaderItem) { + if (false !== strpos($lang, '-')) { + $codes = explode('-', $lang); + if ('i' === $codes[0]) { + // Language not listed in ISO 639 that are not variants + // of any listed language, which can be registered with the + // i-prefix, such as i-cherokee + if (count($codes) > 1) { + $lang = $codes[1]; + } + } else { + for ($i = 0, $max = count($codes); $i < $max; ++$i) { + if (0 === $i) { + $lang = strtolower($codes[0]); + } else { + $lang .= '_'.strtoupper($codes[$i]); + } + } + } + } + + $this->languages[] = $lang; + } + + return $this->languages; + } + + /** + * Gets a list of charsets acceptable by the client browser. + * + * @return array List of charsets in preferable order + */ + public function getCharsets() + { + if (null !== $this->charsets) { + return $this->charsets; + } + + return $this->charsets = array_keys(AcceptHeader::fromString($this->headers->get('Accept-Charset'))->all()); + } + + /** + * Gets a list of encodings acceptable by the client browser. + * + * @return array List of encodings in preferable order + */ + public function getEncodings() + { + if (null !== $this->encodings) { + return $this->encodings; + } + + return $this->encodings = array_keys(AcceptHeader::fromString($this->headers->get('Accept-Encoding'))->all()); + } + + /** + * Gets a list of content types acceptable by the client browser. + * + * @return array List of content types in preferable order + */ + public function getAcceptableContentTypes() + { + if (null !== $this->acceptableContentTypes) { + return $this->acceptableContentTypes; + } + + return $this->acceptableContentTypes = array_keys(AcceptHeader::fromString($this->headers->get('Accept'))->all()); + } + + /** + * Returns true if the request is a XMLHttpRequest. + * + * 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 + * + * @return bool true if the request is an XMLHttpRequest, false otherwise + */ + public function isXmlHttpRequest() + { + return 'XMLHttpRequest' == $this->headers->get('X-Requested-With'); + } + + /* + * 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). + * + * Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com) + */ + + protected function prepareRequestUri() + { + $requestUri = ''; + + if ($this->headers->has('X_ORIGINAL_URL')) { + // IIS with Microsoft Rewrite Module + $requestUri = $this->headers->get('X_ORIGINAL_URL'); + $this->headers->remove('X_ORIGINAL_URL'); + $this->server->remove('HTTP_X_ORIGINAL_URL'); + $this->server->remove('UNENCODED_URL'); + $this->server->remove('IIS_WasUrlRewritten'); + } elseif ($this->headers->has('X_REWRITE_URL')) { + // IIS with ISAPI_Rewrite + $requestUri = $this->headers->get('X_REWRITE_URL'); + $this->headers->remove('X_REWRITE_URL'); + } elseif ('1' == $this->server->get('IIS_WasUrlRewritten') && '' != $this->server->get('UNENCODED_URL')) { + // IIS7 with URL Rewrite: make sure we get the unencoded URL (double slash problem) + $requestUri = $this->server->get('UNENCODED_URL'); + $this->server->remove('UNENCODED_URL'); + $this->server->remove('IIS_WasUrlRewritten'); + } elseif ($this->server->has('REQUEST_URI')) { + $requestUri = $this->server->get('REQUEST_URI'); + // HTTP proxy reqs setup request URI with scheme and host [and port] + the URL path, only use URL path + $schemeAndHttpHost = $this->getSchemeAndHttpHost(); + if (0 === strpos($requestUri, $schemeAndHttpHost)) { + $requestUri = substr($requestUri, strlen($schemeAndHttpHost)); + } + } elseif ($this->server->has('ORIG_PATH_INFO')) { + // IIS 5.0, PHP as CGI + $requestUri = $this->server->get('ORIG_PATH_INFO'); + if ('' != $this->server->get('QUERY_STRING')) { + $requestUri .= '?'.$this->server->get('QUERY_STRING'); + } + $this->server->remove('ORIG_PATH_INFO'); + } + + // normalize the request URI to ease creating sub-requests from this request + $this->server->set('REQUEST_URI', $requestUri); + + return $requestUri; + } + + /** + * Prepares the base URL. + * + * @return string + */ + protected function prepareBaseUrl() + { + $filename = basename($this->server->get('SCRIPT_FILENAME')); + + if (basename($this->server->get('SCRIPT_NAME')) === $filename) { + $baseUrl = $this->server->get('SCRIPT_NAME'); + } elseif (basename($this->server->get('PHP_SELF')) === $filename) { + $baseUrl = $this->server->get('PHP_SELF'); + } elseif (basename($this->server->get('ORIG_SCRIPT_NAME')) === $filename) { + $baseUrl = $this->server->get('ORIG_SCRIPT_NAME'); // 1and1 shared hosting compatibility + } else { + // Backtrack up the script_filename to find the portion matching + // php_self + $path = $this->server->get('PHP_SELF', ''); + $file = $this->server->get('SCRIPT_FILENAME', ''); + $segs = explode('/', trim($file, '/')); + $segs = array_reverse($segs); + $index = 0; + $last = count($segs); + $baseUrl = ''; + do { + $seg = $segs[$index]; + $baseUrl = '/'.$seg.$baseUrl; + ++$index; + } while ($last > $index && (false !== $pos = strpos($path, $baseUrl)) && 0 != $pos); + } + + // Does the baseUrl have anything in common with the request_uri? + $requestUri = $this->getRequestUri(); + if ('' !== $requestUri && '/' !== $requestUri[0]) { + $requestUri = '/'.$requestUri; + } + + if ($baseUrl && false !== $prefix = $this->getUrlencodedPrefix($requestUri, $baseUrl)) { + // full $baseUrl matches + return $prefix; + } + + if ($baseUrl && false !== $prefix = $this->getUrlencodedPrefix($requestUri, rtrim(dirname($baseUrl), '/'.DIRECTORY_SEPARATOR).'/')) { + // directory portion of $baseUrl matches + return rtrim($prefix, '/'.DIRECTORY_SEPARATOR); + } + + $truncatedRequestUri = $requestUri; + if (false !== $pos = strpos($requestUri, '?')) { + $truncatedRequestUri = substr($requestUri, 0, $pos); + } + + $basename = basename($baseUrl); + if (empty($basename) || !strpos(rawurldecode($truncatedRequestUri), $basename)) { + // no match whatsoever; set it blank + return ''; + } + + // If using mod_rewrite or ISAPI_Rewrite strip the script filename + // out of baseUrl. $pos !== 0 makes sure it is not matching a value + // from PATH_INFO or QUERY_STRING + if (strlen($requestUri) >= strlen($baseUrl) && (false !== $pos = strpos($requestUri, $baseUrl)) && 0 !== $pos) { + $baseUrl = substr($requestUri, 0, $pos + strlen($baseUrl)); + } + + return rtrim($baseUrl, '/'.DIRECTORY_SEPARATOR); + } + + /** + * Prepares the base path. + * + * @return string base path + */ + protected function prepareBasePath() + { + $baseUrl = $this->getBaseUrl(); + if (empty($baseUrl)) { + return ''; + } + + $filename = basename($this->server->get('SCRIPT_FILENAME')); + if (basename($baseUrl) === $filename) { + $basePath = dirname($baseUrl); + } else { + $basePath = $baseUrl; + } + + if ('\\' === DIRECTORY_SEPARATOR) { + $basePath = str_replace('\\', '/', $basePath); + } + + return rtrim($basePath, '/'); + } + + /** + * Prepares the path info. + * + * @return string path info + */ + protected function preparePathInfo() + { + if (null === ($requestUri = $this->getRequestUri())) { + return '/'; + } + + // Remove the query string from REQUEST_URI + if (false !== $pos = strpos($requestUri, '?')) { + $requestUri = substr($requestUri, 0, $pos); + } + if ('' !== $requestUri && '/' !== $requestUri[0]) { + $requestUri = '/'.$requestUri; + } + + if (null === ($baseUrl = $this->getBaseUrl())) { + return $requestUri; + } + + $pathInfo = substr($requestUri, strlen($baseUrl)); + if (false === $pathInfo || '' === $pathInfo) { + // If substr() returns false then PATH_INFO is set to an empty string + return '/'; + } + + return (string) $pathInfo; + } + + /** + * Initializes HTTP request formats. + */ + 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'), + ); + } + + /** + * Sets the default PHP locale. + * + * @param string $locale + */ + private function setPhpDefaultLocale($locale) + { + // 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 + // the call can be ignored: + try { + if (class_exists('Locale', false)) { + \Locale::setDefault($locale); + } + } catch (\Exception $e) { + } + } + + /* + * 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 + */ + private function getUrlencodedPrefix($string, $prefix) + { + if (0 !== strpos(rawurldecode($string), $prefix)) { + return false; + } + + $len = strlen($prefix); + + if (preg_match(sprintf('#^(%%[[:xdigit:]]{2}|.){%d}#', $len), $string, $match)) { + return $match[0]; + } + + return false; + } + + private static function createRequestFromFactory(array $query = array(), array $request = array(), array $attributes = array(), array $cookies = array(), array $files = array(), array $server = array(), $content = null) + { + if (self::$requestFactory) { + $request = call_user_func(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.'); + } + + return $request; + } + + return new static($query, $request, $attributes, $cookies, $files, $server, $content); + } + + /** + * Indicates whether this request originated from a trusted proxy. + * + * This can be useful to determine whether or not to trust the + * contents of a proxy-specific header. + * + * @return bool true if the request came from a trusted proxy, false otherwise + */ + public function isFromTrustedProxy() + { + return self::$trustedProxies && IpUtils::checkIp($this->server->get('REMOTE_ADDR'), self::$trustedProxies); + } + + private function getTrustedValues($type, $ip = null) + { + $clientValues = array(); + $forwardedValues = array(); + + if (self::$trustedHeaders[$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); + } + } + + 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 (null !== $ip) { + $clientValues = $this->normalizeAndFilterClientIps($clientValues, $ip); + $forwardedValues = $this->normalizeAndFilterClientIps($forwardedValues, $ip); + } + + if ($forwardedValues === $clientValues || !$clientValues) { + return $forwardedValues; + } + + if (!$forwardedValues) { + return $clientValues; + } + + if (!$this->isForwardedValid) { + return null !== $ip ? array('0.0.0.0', $ip) : array(); + } + $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) + { + if (!$clientIps) { + return array(); + } + $clientIps[] = $ip; // Complete the IP chain with the IP the request actually came from + $firstTrustedIp = null; + + foreach ($clientIps as $key => $clientIp) { + // Remove port (unfortunately, it does happen) + if (preg_match('{((?:\d+\.){3}\d+)\:\d+}', $clientIp, $match)) { + $clientIps[$key] = $clientIp = $match[1]; + } + + if (!filter_var($clientIp, FILTER_VALIDATE_IP)) { + unset($clientIps[$key]); + + continue; + } + + if (IpUtils::checkIp($clientIp, self::$trustedProxies)) { + unset($clientIps[$key]); + + // Fallback to this when the client IP falls into the range of trusted proxies + if (null === $firstTrustedIp) { + $firstTrustedIp = $clientIp; + } + } + } + + // Now the IP chain contains only untrusted proxies and the client IP + return $clientIps ? array_reverse($clientIps) : array($firstTrustedIp); + } +} diff --git a/lib/silex/vendor/symfony/http-foundation/RequestMatcher.php b/lib/silex/vendor/symfony/http-foundation/RequestMatcher.php new file mode 100644 index 000000000..076d077c7 --- /dev/null +++ b/lib/silex/vendor/symfony/http-foundation/RequestMatcher.php @@ -0,0 +1,178 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation; + +/** + * RequestMatcher compares a pre-defined set of checks against a Request instance. + * + * @author Fabien Potencier + */ +class RequestMatcher implements RequestMatcherInterface +{ + /** + * @var string|null + */ + private $path; + + /** + * @var string|null + */ + private $host; + + /** + * @var string[] + */ + private $methods = array(); + + /** + * @var string[] + */ + private $ips = array(); + + /** + * @var array + */ + private $attributes = array(); + + /** + * @var string[] + */ + private $schemes = array(); + + /** + * @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) + { + $this->matchPath($path); + $this->matchHost($host); + $this->matchMethod($methods); + $this->matchIps($ips); + $this->matchScheme($schemes); + + foreach ($attributes as $k => $v) { + $this->matchAttribute($k, $v); + } + } + + /** + * Adds a check for the HTTP scheme. + * + * @param string|string[]|null $scheme An HTTP scheme or an array of HTTP schemes + */ + public function matchScheme($scheme) + { + $this->schemes = null !== $scheme ? array_map('strtolower', (array) $scheme) : array(); + } + + /** + * Adds a check for the URL host name. + * + * @param string|null $regexp A Regexp + */ + public function matchHost($regexp) + { + $this->host = $regexp; + } + + /** + * Adds a check for the URL path info. + * + * @param string|null $regexp A Regexp + */ + public function matchPath($regexp) + { + $this->path = $regexp; + } + + /** + * Adds a check for the client IP. + * + * @param string $ip A specific IP address or a range specified using IP/netmask like 192.168.1.0/24 + */ + public function matchIp($ip) + { + $this->matchIps($ip); + } + + /** + * Adds a check for the client IP. + * + * @param string|string[]|null $ips A specific IP address or a range specified using IP/netmask like 192.168.1.0/24 + */ + public function matchIps($ips) + { + $this->ips = null !== $ips ? (array) $ips : array(); + } + + /** + * Adds a check for the HTTP method. + * + * @param string|string[]|null $method An HTTP method or an array of HTTP methods + */ + public function matchMethod($method) + { + $this->methods = null !== $method ? array_map('strtoupper', (array) $method) : array(); + } + + /** + * Adds a check for request attribute. + * + * @param string $key The request attribute name + * @param string $regexp A Regexp + */ + public function matchAttribute($key, $regexp) + { + $this->attributes[$key] = $regexp; + } + + /** + * {@inheritdoc} + */ + public function matches(Request $request) + { + if ($this->schemes && !in_array($request->getScheme(), $this->schemes, true)) { + return false; + } + + if ($this->methods && !in_array($request->getMethod(), $this->methods, true)) { + return false; + } + + foreach ($this->attributes as $key => $pattern) { + if (!preg_match('{'.$pattern.'}', $request->attributes->get($key))) { + return false; + } + } + + if (null !== $this->path && !preg_match('{'.$this->path.'}', rawurldecode($request->getPathInfo()))) { + return false; + } + + if (null !== $this->host && !preg_match('{'.$this->host.'}i', $request->getHost())) { + return false; + } + + if (IpUtils::checkIp($request->getClientIp(), $this->ips)) { + return true; + } + + // Note to future implementors: add additional checks above the + // foreach above or else your check might not be run! + return 0 === count($this->ips); + } +} diff --git a/lib/silex/vendor/symfony/http-foundation/RequestMatcherInterface.php b/lib/silex/vendor/symfony/http-foundation/RequestMatcherInterface.php new file mode 100644 index 000000000..c26db3e6f --- /dev/null +++ b/lib/silex/vendor/symfony/http-foundation/RequestMatcherInterface.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation; + +/** + * RequestMatcherInterface is an interface for strategies to match a Request. + * + * @author Fabien Potencier + */ +interface RequestMatcherInterface +{ + /** + * Decides whether the rule(s) implemented by the strategy matches the supplied request. + * + * @return bool true if the request matches, false otherwise + */ + public function matches(Request $request); +} diff --git a/lib/silex/vendor/symfony/http-foundation/RequestStack.php b/lib/silex/vendor/symfony/http-foundation/RequestStack.php new file mode 100644 index 000000000..3d9cfd0c6 --- /dev/null +++ b/lib/silex/vendor/symfony/http-foundation/RequestStack.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\HttpFoundation; + +/** + * Request stack that controls the lifecycle of requests. + * + * @author Benjamin Eberlei + */ +class RequestStack +{ + /** + * @var Request[] + */ + private $requests = array(); + + /** + * Pushes a Request on the stack. + * + * This method should generally not be called directly as the stack + * management should be taken care of by the application itself. + */ + public function push(Request $request) + { + $this->requests[] = $request; + } + + /** + * Pops the current request from the stack. + * + * This operation lets the current request go out of scope. + * + * This method should generally not be called directly as the stack + * management should be taken care of by the application itself. + * + * @return Request|null + */ + public function pop() + { + if (!$this->requests) { + return; + } + + return array_pop($this->requests); + } + + /** + * @return Request|null + */ + public function getCurrentRequest() + { + return end($this->requests) ?: null; + } + + /** + * Gets the master Request. + * + * Be warned that making your code aware of the master request + * might make it un-compatible with other features of your framework + * like ESI support. + * + * @return Request|null + */ + public function getMasterRequest() + { + if (!$this->requests) { + return; + } + + return $this->requests[0]; + } + + /** + * Returns the parent request of the current. + * + * Be warned that making your code aware of the parent request + * might make it un-compatible with other features of your framework + * like ESI support. + * + * If current Request is the master request, it returns null. + * + * @return Request|null + */ + public function getParentRequest() + { + $pos = count($this->requests) - 2; + + if (!isset($this->requests[$pos])) { + return; + } + + return $this->requests[$pos]; + } +} diff --git a/lib/silex/vendor/symfony/http-foundation/Response.php b/lib/silex/vendor/symfony/http-foundation/Response.php new file mode 100644 index 000000000..cf1325afe --- /dev/null +++ b/lib/silex/vendor/symfony/http-foundation/Response.php @@ -0,0 +1,1298 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation; + +/** + * Response represents an HTTP response. + * + * @author Fabien Potencier + */ +class Response +{ + const HTTP_CONTINUE = 100; + const HTTP_SWITCHING_PROTOCOLS = 101; + const HTTP_PROCESSING = 102; // RFC2518 + const HTTP_OK = 200; + const HTTP_CREATED = 201; + const HTTP_ACCEPTED = 202; + const HTTP_NON_AUTHORITATIVE_INFORMATION = 203; + const HTTP_NO_CONTENT = 204; + const HTTP_RESET_CONTENT = 205; + const HTTP_PARTIAL_CONTENT = 206; + const HTTP_MULTI_STATUS = 207; // RFC4918 + const HTTP_ALREADY_REPORTED = 208; // RFC5842 + const HTTP_IM_USED = 226; // RFC3229 + const HTTP_MULTIPLE_CHOICES = 300; + const HTTP_MOVED_PERMANENTLY = 301; + const HTTP_FOUND = 302; + const HTTP_SEE_OTHER = 303; + const HTTP_NOT_MODIFIED = 304; + const HTTP_USE_PROXY = 305; + const HTTP_RESERVED = 306; + const HTTP_TEMPORARY_REDIRECT = 307; + const HTTP_PERMANENTLY_REDIRECT = 308; // RFC7238 + const HTTP_BAD_REQUEST = 400; + const HTTP_UNAUTHORIZED = 401; + const HTTP_PAYMENT_REQUIRED = 402; + const HTTP_FORBIDDEN = 403; + const HTTP_NOT_FOUND = 404; + const HTTP_METHOD_NOT_ALLOWED = 405; + const HTTP_NOT_ACCEPTABLE = 406; + const HTTP_PROXY_AUTHENTICATION_REQUIRED = 407; + const HTTP_REQUEST_TIMEOUT = 408; + const HTTP_CONFLICT = 409; + const HTTP_GONE = 410; + const HTTP_LENGTH_REQUIRED = 411; + const HTTP_PRECONDITION_FAILED = 412; + const HTTP_REQUEST_ENTITY_TOO_LARGE = 413; + const HTTP_REQUEST_URI_TOO_LONG = 414; + const HTTP_UNSUPPORTED_MEDIA_TYPE = 415; + const HTTP_REQUESTED_RANGE_NOT_SATISFIABLE = 416; + const HTTP_EXPECTATION_FAILED = 417; + const HTTP_I_AM_A_TEAPOT = 418; // RFC2324 + const HTTP_MISDIRECTED_REQUEST = 421; // RFC7540 + const HTTP_UNPROCESSABLE_ENTITY = 422; // RFC4918 + const HTTP_LOCKED = 423; // RFC4918 + const HTTP_FAILED_DEPENDENCY = 424; // RFC4918 + const HTTP_RESERVED_FOR_WEBDAV_ADVANCED_COLLECTIONS_EXPIRED_PROPOSAL = 425; // RFC2817 + const HTTP_UPGRADE_REQUIRED = 426; // RFC2817 + const HTTP_PRECONDITION_REQUIRED = 428; // RFC6585 + const HTTP_TOO_MANY_REQUESTS = 429; // RFC6585 + const HTTP_REQUEST_HEADER_FIELDS_TOO_LARGE = 431; // RFC6585 + const HTTP_UNAVAILABLE_FOR_LEGAL_REASONS = 451; + const HTTP_INTERNAL_SERVER_ERROR = 500; + const HTTP_NOT_IMPLEMENTED = 501; + const HTTP_BAD_GATEWAY = 502; + const HTTP_SERVICE_UNAVAILABLE = 503; + const HTTP_GATEWAY_TIMEOUT = 504; + const HTTP_VERSION_NOT_SUPPORTED = 505; + const HTTP_VARIANT_ALSO_NEGOTIATES_EXPERIMENTAL = 506; // RFC2295 + const HTTP_INSUFFICIENT_STORAGE = 507; // RFC4918 + const HTTP_LOOP_DETECTED = 508; // RFC5842 + const HTTP_NOT_EXTENDED = 510; // RFC2774 + const HTTP_NETWORK_AUTHENTICATION_REQUIRED = 511; // RFC6585 + + /** + * @var \Symfony\Component\HttpFoundation\ResponseHeaderBag + */ + public $headers; + + /** + * @var string + */ + protected $content; + + /** + * @var string + */ + protected $version; + + /** + * @var int + */ + protected $statusCode; + + /** + * @var string + */ + protected $statusText; + + /** + * @var string + */ + protected $charset; + + /** + * 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} + * (last updated 2016-03-01). + * + * Unless otherwise noted, the status code is defined in RFC2616. + * + * @var array + */ + public static $statusTexts = array( + 100 => 'Continue', + 101 => 'Switching Protocols', + 102 => 'Processing', // RFC2518 + 103 => 'Early Hints', + 200 => 'OK', + 201 => 'Created', + 202 => 'Accepted', + 203 => 'Non-Authoritative Information', + 204 => 'No Content', + 205 => 'Reset Content', + 206 => 'Partial Content', + 207 => 'Multi-Status', // RFC4918 + 208 => 'Already Reported', // RFC5842 + 226 => 'IM Used', // RFC3229 + 300 => 'Multiple Choices', + 301 => 'Moved Permanently', + 302 => 'Found', + 303 => 'See Other', + 304 => 'Not Modified', + 305 => 'Use Proxy', + 307 => 'Temporary Redirect', + 308 => 'Permanent Redirect', // RFC7238 + 400 => 'Bad Request', + 401 => 'Unauthorized', + 402 => 'Payment Required', + 403 => 'Forbidden', + 404 => 'Not Found', + 405 => 'Method Not Allowed', + 406 => 'Not Acceptable', + 407 => 'Proxy Authentication Required', + 408 => 'Request Timeout', + 409 => 'Conflict', + 410 => 'Gone', + 411 => 'Length Required', + 412 => 'Precondition Failed', + 413 => 'Payload Too Large', + 414 => 'URI Too Long', + 415 => 'Unsupported Media Type', + 416 => 'Range Not Satisfiable', + 417 => 'Expectation Failed', + 418 => 'I\'m a teapot', // RFC2324 + 421 => 'Misdirected Request', // RFC7540 + 422 => 'Unprocessable Entity', // RFC4918 + 423 => 'Locked', // RFC4918 + 424 => 'Failed Dependency', // RFC4918 + 425 => 'Reserved for WebDAV advanced collections expired proposal', // RFC2817 + 426 => 'Upgrade Required', // RFC2817 + 428 => 'Precondition Required', // RFC6585 + 429 => 'Too Many Requests', // RFC6585 + 431 => 'Request Header Fields Too Large', // RFC6585 + 451 => 'Unavailable For Legal Reasons', // RFC7725 + 500 => 'Internal Server Error', + 501 => 'Not Implemented', + 502 => 'Bad Gateway', + 503 => 'Service Unavailable', + 504 => 'Gateway Timeout', + 505 => 'HTTP Version Not Supported', + 506 => 'Variant Also Negotiates', // RFC2295 + 507 => 'Insufficient Storage', // RFC4918 + 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()) + { + $this->headers = new ResponseHeaderBag($headers); + $this->setContent($content); + $this->setStatusCode($status); + $this->setProtocolVersion('1.0'); + } + + /** + * Factory method for chainability. + * + * Example: + * + * return Response::create($body, 200) + * ->setSharedMaxAge(300); + * + * @param mixed $content The response content, see setContent() + * @param int $status The response status code + * @param array $headers An array of response headers + * + * @return static + */ + public static function create($content = '', $status = 200, $headers = array()) + { + return new static($content, $status, $headers); + } + + /** + * Returns the Response as an HTTP string. + * + * The string representation of the Response is the same as the + * one that will be sent to the client only if the prepare() method + * has been called before. + * + * @return string The Response as an HTTP string + * + * @see prepare() + */ + public function __toString() + { + return + sprintf('HTTP/%s %s %s', $this->version, $this->statusCode, $this->statusText)."\r\n". + $this->headers."\r\n". + $this->getContent(); + } + + /** + * Clones the current Response instance. + */ + public function __clone() + { + $this->headers = clone $this->headers; + } + + /** + * Prepares the Response before it is sent to the client. + * + * This method tweaks the Response to ensure that it is + * compliant with RFC 2616. Most of the changes are based on + * the Request that is "associated" with this Response. + * + * @return $this + */ + public function prepare(Request $request) + { + $headers = $this->headers; + + if ($this->isInformational() || $this->isEmpty()) { + $this->setContent(null); + $headers->remove('Content-Type'); + $headers->remove('Content-Length'); + } else { + // Content-type based on the Request + if (!$headers->has('Content-Type')) { + $format = $request->getRequestFormat(); + if (null !== $format && $mimeType = $request->getMimeType($format)) { + $headers->set('Content-Type', $mimeType); + } + } + + // Fix Content-Type + $charset = $this->charset ?: 'UTF-8'; + if (!$headers->has('Content-Type')) { + $headers->set('Content-Type', 'text/html; charset='.$charset); + } elseif (0 === stripos($headers->get('Content-Type'), 'text/') && false === stripos($headers->get('Content-Type'), 'charset')) { + // add the charset + $headers->set('Content-Type', $headers->get('Content-Type').'; charset='.$charset); + } + + // Fix Content-Length + if ($headers->has('Transfer-Encoding')) { + $headers->remove('Content-Length'); + } + + if ($request->isMethod('HEAD')) { + // cf. RFC2616 14.13 + $length = $headers->get('Content-Length'); + $this->setContent(null); + if ($length) { + $headers->set('Content-Length', $length); + } + } + } + + // Fix protocol + if ('HTTP/1.0' != $request->server->get('SERVER_PROTOCOL')) { + $this->setProtocolVersion('1.1'); + } + + // 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); + } + + $this->ensureIEOverSSLCompatibility($request); + + return $this; + } + + /** + * Sends HTTP headers. + * + * @return $this + */ + public function sendHeaders() + { + // headers have already been sent by the developer + if (headers_sent()) { + return $this; + } + + // headers + foreach ($this->headers->allPreserveCaseWithoutCookies() as $name => $values) { + foreach ($values as $value) { + header($name.': '.$value, false, $this->statusCode); + } + } + + // status + header(sprintf('HTTP/%s %s %s', $this->version, $this->statusCode, $this->statusText), true, $this->statusCode); + + // cookies + foreach ($this->headers->getCookies() as $cookie) { + if ($cookie->isRaw()) { + setrawcookie($cookie->getName(), $cookie->getValue(), $cookie->getExpiresTime(), $cookie->getPath(), $cookie->getDomain(), $cookie->isSecure(), $cookie->isHttpOnly()); + } else { + setcookie($cookie->getName(), $cookie->getValue(), $cookie->getExpiresTime(), $cookie->getPath(), $cookie->getDomain(), $cookie->isSecure(), $cookie->isHttpOnly()); + } + } + + return $this; + } + + /** + * Sends content for the current web response. + * + * @return $this + */ + public function sendContent() + { + echo $this->content; + + return $this; + } + + /** + * Sends HTTP headers and content. + * + * @return $this + */ + public function send() + { + $this->sendHeaders(); + $this->sendContent(); + + if (function_exists('fastcgi_finish_request')) { + fastcgi_finish_request(); + } elseif ('cli' !== PHP_SAPI) { + static::closeOutputBuffers(0, true); + } + + return $this; + } + + /** + * Sets the response content. + * + * Valid types are strings, numbers, null, and objects that implement a __toString() method. + * + * @param mixed $content Content that can be cast to string + * + * @return $this + * + * @throws \UnexpectedValueException + */ + public function setContent($content) + { + if (null !== $content && !is_string($content) && !is_numeric($content) && !is_callable(array($content, '__toString'))) { + throw new \UnexpectedValueException(sprintf('The Response content must be a string or object implementing __toString(), "%s" given.', gettype($content))); + } + + $this->content = (string) $content; + + return $this; + } + + /** + * Gets the current response content. + * + * @return string Content + */ + public function getContent() + { + return $this->content; + } + + /** + * Sets the HTTP protocol version (1.0 or 1.1). + * + * @param string $version The HTTP protocol version + * + * @return $this + * + * @final since version 3.2 + */ + public function setProtocolVersion($version) + { + $this->version = $version; + + return $this; + } + + /** + * Gets the HTTP protocol version. + * + * @return string The HTTP protocol version + * + * @final since version 3.2 + */ + public function getProtocolVersion() + { + return $this->version; + } + + /** + * Sets the response status code. + * + * 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 + */ + public function setStatusCode($code, $text = null) + { + $this->statusCode = $code = (int) $code; + if ($this->isInvalid()) { + throw new \InvalidArgumentException(sprintf('The HTTP status code "%s" is not valid.', $code)); + } + + if (null === $text) { + $this->statusText = isset(self::$statusTexts[$code]) ? self::$statusTexts[$code] : 'unknown status'; + + return $this; + } + + if (false === $text) { + $this->statusText = ''; + + return $this; + } + + $this->statusText = $text; + + return $this; + } + + /** + * Retrieves the status code for the current web response. + * + * @return int Status code + * + * @final since version 3.2 + */ + public function getStatusCode() + { + return $this->statusCode; + } + + /** + * Sets the response charset. + * + * @param string $charset Character set + * + * @return $this + * + * @final since version 3.2 + */ + public function setCharset($charset) + { + $this->charset = $charset; + + return $this; + } + + /** + * Retrieves the response charset. + * + * @return string Character set + * + * @final since version 3.2 + */ + public function getCharset() + { + return $this->charset; + } + + /** + * Returns true if the response is worth caching under any circumstance. + * + * Responses marked "private" with an explicit Cache-Control directive are + * considered uncacheable. + * + * Responses with neither a freshness lifetime (Expires, max-age) nor cache + * validator (Last-Modified, ETag) are considered uncacheable. + * + * @return bool true if the response is worth caching, false otherwise + * + * @final since version 3.3 + */ + public function isCacheable() + { + if (!in_array($this->statusCode, array(200, 203, 300, 301, 302, 404, 410))) { + return false; + } + + if ($this->headers->hasCacheControlDirective('no-store') || $this->headers->getCacheControlDirective('private')) { + return false; + } + + return $this->isValidateable() || $this->isFresh(); + } + + /** + * Returns true if the response is "fresh". + * + * Fresh responses may be served from cache without any interaction with the + * 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 + */ + public function isFresh() + { + return $this->getTtl() > 0; + } + + /** + * 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 + */ + public function isValidateable() + { + return $this->headers->has('Last-Modified') || $this->headers->has('ETag'); + } + + /** + * Marks the response as "private". + * + * It makes the response ineligible for serving other clients. + * + * @return $this + * + * @final since version 3.2 + */ + public function setPrivate() + { + $this->headers->removeCacheControlDirective('public'); + $this->headers->addCacheControlDirective('private'); + + return $this; + } + + /** + * Marks the response as "public". + * + * It makes the response eligible for serving other clients. + * + * @return $this + * + * @final since version 3.2 + */ + public function setPublic() + { + $this->headers->addCacheControlDirective('public'); + $this->headers->removeCacheControlDirective('private'); + + return $this; + } + + /** + * Marks the response as "immutable". + * + * @param bool $immutable enables or disables the immutable directive + * + * @return $this + * + * @final + */ + public function setImmutable($immutable = true) + { + if ($immutable) { + $this->headers->addCacheControlDirective('immutable'); + } else { + $this->headers->removeCacheControlDirective('immutable'); + } + + return $this; + } + + /** + * 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() + { + return $this->headers->hasCacheControlDirective('immutable'); + } + + /** + * Returns true if the response must be revalidated by caches. + * + * 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 + */ + public function mustRevalidate() + { + return $this->headers->hasCacheControlDirective('must-revalidate') || $this->headers->hasCacheControlDirective('proxy-revalidate'); + } + + /** + * 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 + */ + public function getDate() + { + return $this->headers->getDate('Date'); + } + + /** + * Sets the Date header. + * + * @return $this + * + * @final since version 3.2 + */ + public function setDate(\DateTime $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 + * + * @final since version 3.2 + */ + public function getAge() + { + if (null !== $age = $this->headers->get('Age')) { + return (int) $age; + } + + return max(time() - $this->getDate()->format('U'), 0); + } + + /** + * Marks the response stale by setting the Age header to be equal to the maximum age of the response. + * + * @return $this + */ + public function expire() + { + if ($this->isFresh()) { + $this->headers->set('Age', $this->getMaxAge()); + } + + return $this; + } + + /** + * 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 + */ + public function getExpires() + { + 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'); + } + } + + /** + * Sets the Expires HTTP header with a DateTime instance. + * + * 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 + */ + public function setExpires(\DateTime $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; + } + + /** + * Returns the number of seconds after the time specified in the response's Date + * header when the response should no longer be considered fresh. + * + * 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 + */ + public function getMaxAge() + { + if ($this->headers->hasCacheControlDirective('s-maxage')) { + return (int) $this->headers->getCacheControlDirective('s-maxage'); + } + + if ($this->headers->hasCacheControlDirective('max-age')) { + return (int) $this->headers->getCacheControlDirective('max-age'); + } + + if (null !== $this->getExpires()) { + return $this->getExpires()->format('U') - $this->getDate()->format('U'); + } + } + + /** + * Sets the number of seconds after which the response should no longer be considered fresh. + * + * This methods sets the Cache-Control max-age directive. + * + * @param int $value Number of seconds + * + * @return $this + * + * @final since version 3.2 + */ + public function setMaxAge($value) + { + $this->headers->addCacheControlDirective('max-age', $value); + + return $this; + } + + /** + * Sets the number of seconds after which the response should no longer be considered fresh by shared caches. + * + * This methods sets the Cache-Control s-maxage directive. + * + * @param int $value Number of seconds + * + * @return $this + * + * @final since version 3.2 + */ + public function setSharedMaxAge($value) + { + $this->setPublic(); + $this->headers->addCacheControlDirective('s-maxage', $value); + + return $this; + } + + /** + * Returns the response's time-to-live in seconds. + * + * It returns null when no freshness information is present in the response. + * + * 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 + */ + public function getTtl() + { + if (null !== $maxAge = $this->getMaxAge()) { + return $maxAge - $this->getAge(); + } + } + + /** + * Sets the response's time-to-live for shared caches. + * + * This method adjusts the Cache-Control/s-maxage directive. + * + * @param int $seconds Number of seconds + * + * @return $this + * + * @final since version 3.2 + */ + public function setTtl($seconds) + { + $this->setSharedMaxAge($this->getAge() + $seconds); + + return $this; + } + + /** + * Sets the response's time-to-live for private/client caches. + * + * This method adjusts the Cache-Control/max-age directive. + * + * @param int $seconds Number of seconds + * + * @return $this + * + * @final since version 3.2 + */ + public function setClientTtl($seconds) + { + $this->setMaxAge($this->getAge() + $seconds); + + return $this; + } + + /** + * 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 + */ + public function getLastModified() + { + return $this->headers->getDate('Last-Modified'); + } + + /** + * Sets the Last-Modified HTTP header with a DateTime instance. + * + * 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 + */ + public function setLastModified(\DateTime $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; + } + + /** + * 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 + */ + public function getEtag() + { + return $this->headers->get('ETag'); + } + + /** + * Sets the ETag value. + * + * @param string|null $etag The ETag unique identifier or null to remove the header + * @param bool $weak Whether you want a weak ETag or not + * + * @return $this + * + * @final since version 3.2 + */ + public function setEtag($etag = null, $weak = false) + { + if (null === $etag) { + $this->headers->remove('Etag'); + } else { + if (0 !== strpos($etag, '"')) { + $etag = '"'.$etag.'"'; + } + + $this->headers->set('ETag', (true === $weak ? 'W/' : '').$etag); + } + + return $this; + } + + /** + * Sets the response's cache headers (validation and/or expiration). + * + * 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 + */ + public function setCache(array $options) + { + if ($diff = array_diff(array_keys($options), array('etag', 'last_modified', 'max_age', 's_maxage', 'private', 'public', 'immutable'))) { + throw new \InvalidArgumentException(sprintf('Response does not support the following options: "%s".', implode('", "', array_values($diff)))); + } + + if (isset($options['etag'])) { + $this->setEtag($options['etag']); + } + + if (isset($options['last_modified'])) { + $this->setLastModified($options['last_modified']); + } + + if (isset($options['max_age'])) { + $this->setMaxAge($options['max_age']); + } + + if (isset($options['s_maxage'])) { + $this->setSharedMaxAge($options['s_maxage']); + } + + if (isset($options['public'])) { + if ($options['public']) { + $this->setPublic(); + } else { + $this->setPrivate(); + } + } + + if (isset($options['private'])) { + if ($options['private']) { + $this->setPrivate(); + } else { + $this->setPublic(); + } + } + + if (isset($options['immutable'])) { + $this->setImmutable((bool) $options['immutable']); + } + + return $this; + } + + /** + * Modifies the response so that it conforms to the rules defined for a 304 status code. + * + * This sets the status, removes the body, and discards any headers + * that MUST NOT be included in 304 responses. + * + * @return $this + * + * @see http://tools.ietf.org/html/rfc2616#section-10.3.5 + * + * @final since version 3.3 + */ + public function setNotModified() + { + $this->setStatusCode(304); + $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) { + $this->headers->remove($header); + } + + return $this; + } + + /** + * 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 + */ + public function hasVary() + { + return null !== $this->headers->get('Vary'); + } + + /** + * Returns an array of header names given in the Vary header. + * + * @return array An array of Vary names + * + * @final since version 3.2 + */ + public function getVary() + { + if (!$vary = $this->headers->get('Vary', null, false)) { + return array(); + } + + $ret = array(); + foreach ($vary as $item) { + $ret = array_merge($ret, preg_split('/[\s,]+/', $item)); + } + + return $ret; + } + + /** + * Sets the Vary header. + * + * @param string|array $headers + * @param bool $replace Whether to replace the actual value or not (true by default) + * + * @return $this + * + * @final since version 3.2 + */ + public function setVary($headers, $replace = true) + { + $this->headers->set('Vary', $headers, $replace); + + return $this; + } + + /** + * Determines if the Response validators (ETag, Last-Modified) match + * a conditional value specified in the Request. + * + * If the Response is not modified, it sets the status code to 304 and + * removes the actual content by calling the setNotModified() method. + * + * @return bool true if the Response validators match the Request, false otherwise + * + * @final since version 3.3 + */ + public function isNotModified(Request $request) + { + if (!$request->isMethodCacheable()) { + return false; + } + + $notModified = false; + $lastModified = $this->headers->get('Last-Modified'); + $modifiedSince = $request->headers->get('If-Modified-Since'); + + if ($etags = $request->getETags()) { + $notModified = in_array($this->getEtag(), $etags) || in_array('*', $etags); + } + + if ($modifiedSince && $lastModified) { + $notModified = strtotime($modifiedSince) >= strtotime($lastModified) && (!$etags || $notModified); + } + + if ($notModified) { + $this->setNotModified(); + } + + return $notModified; + } + + /** + * Is response invalid? + * + * @return bool + * + * @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html + * + * @final since version 3.2 + */ + public function isInvalid() + { + return $this->statusCode < 100 || $this->statusCode >= 600; + } + + /** + * Is response informative? + * + * @return bool + * + * @final since version 3.3 + */ + public function isInformational() + { + return $this->statusCode >= 100 && $this->statusCode < 200; + } + + /** + * Is response successful? + * + * @return bool + * + * @final since version 3.2 + */ + public function isSuccessful() + { + return $this->statusCode >= 200 && $this->statusCode < 300; + } + + /** + * Is the response a redirect? + * + * @return bool + * + * @final since version 3.2 + */ + public function isRedirection() + { + return $this->statusCode >= 300 && $this->statusCode < 400; + } + + /** + * Is there a client error? + * + * @return bool + * + * @final since version 3.2 + */ + public function isClientError() + { + return $this->statusCode >= 400 && $this->statusCode < 500; + } + + /** + * Was there a server side error? + * + * @return bool + * + * @final since version 3.3 + */ + public function isServerError() + { + return $this->statusCode >= 500 && $this->statusCode < 600; + } + + /** + * Is the response OK? + * + * @return bool + * + * @final since version 3.2 + */ + public function isOk() + { + return 200 === $this->statusCode; + } + + /** + * Is the response forbidden? + * + * @return bool + * + * @final since version 3.2 + */ + public function isForbidden() + { + return 403 === $this->statusCode; + } + + /** + * Is the response a not found error? + * + * @return bool + * + * @final since version 3.2 + */ + public function isNotFound() + { + return 404 === $this->statusCode; + } + + /** + * Is the response a redirect of some form? + * + * @param string $location + * + * @return bool + * + * @final since version 3.2 + */ + public function isRedirect($location = null) + { + return in_array($this->statusCode, array(201, 301, 302, 303, 307, 308)) && (null === $location ?: $location == $this->headers->get('Location')); + } + + /** + * Is the response empty? + * + * @return bool + * + * @final since version 3.2 + */ + public function isEmpty() + { + return in_array($this->statusCode, array(204, 304)); + } + + /** + * Cleans or flushes output buffers up to target level. + * + * 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 + */ + public static function closeOutputBuffers($targetLevel, $flush) + { + $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; + + while ($level-- > $targetLevel && ($s = $status[$level]) && (!isset($s['del']) ? !isset($s['flags']) || ($s['flags'] & $flags) === $flags : $s['del'])) { + if ($flush) { + ob_end_flush(); + } else { + ob_end_clean(); + } + } + } + + /** + * Checks if we need to remove Cache-Control for SSL encrypted downloads when using IE < 9. + * + * @see http://support.microsoft.com/kb/323308 + * + * @final since version 3.3 + */ + protected function ensureIEOverSSLCompatibility(Request $request) + { + 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) { + $this->headers->remove('Cache-Control'); + } + } + } +} diff --git a/lib/silex/vendor/symfony/http-foundation/ResponseHeaderBag.php b/lib/silex/vendor/symfony/http-foundation/ResponseHeaderBag.php new file mode 100644 index 000000000..11a859326 --- /dev/null +++ b/lib/silex/vendor/symfony/http-foundation/ResponseHeaderBag.php @@ -0,0 +1,340 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation; + +/** + * ResponseHeaderBag is a container for Response HTTP headers. + * + * @author Fabien Potencier + */ +class ResponseHeaderBag extends HeaderBag +{ + const COOKIES_FLAT = 'flat'; + const COOKIES_ARRAY = 'array'; + + const DISPOSITION_ATTACHMENT = 'attachment'; + const DISPOSITION_INLINE = 'inline'; + + protected $computedCacheControl = array(); + protected $cookies = array(); + protected $headerNames = array(); + + public function __construct(array $headers = array()) + { + parent::__construct($headers); + + if (!isset($this->headers['cache-control'])) { + $this->set('Cache-Control', ''); + } + + /* RFC2616 - 14.18 says all Responses need to have a Date */ + if (!isset($this->headers['date'])) { + $this->initDate(); + } + } + + /** + * Returns the headers, with original capitalizations. + * + * @return array An array of headers + */ + public function allPreserveCase() + { + $headers = array(); + foreach ($this->all() as $name => $value) { + $headers[isset($this->headerNames[$name]) ? $this->headerNames[$name] : $name] = $value; + } + + return $headers; + } + + public function allPreserveCaseWithoutCookies() + { + $headers = $this->allPreserveCase(); + if (isset($this->headerNames['set-cookie'])) { + unset($headers[$this->headerNames['set-cookie']]); + } + + return $headers; + } + + /** + * {@inheritdoc} + */ + public function replace(array $headers = array()) + { + $this->headerNames = array(); + + parent::replace($headers); + + if (!isset($this->headers['cache-control'])) { + $this->set('Cache-Control', ''); + } + + if (!isset($this->headers['date'])) { + $this->initDate(); + } + } + + /** + * {@inheritdoc} + */ + public function all() + { + $headers = parent::all(); + foreach ($this->getCookies() as $cookie) { + $headers['set-cookie'][] = (string) $cookie; + } + + return $headers; + } + + /** + * {@inheritdoc} + */ + public function set($key, $values, $replace = true) + { + $uniqueKey = str_replace('_', '-', strtolower($key)); + + if ('set-cookie' === $uniqueKey) { + if ($replace) { + $this->cookies = array(); + } + foreach ((array) $values as $cookie) { + $this->setCookie(Cookie::fromString($cookie)); + } + $this->headerNames[$uniqueKey] = $key; + + return; + } + + $this->headerNames[$uniqueKey] = $key; + + 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); + $this->headerNames['cache-control'] = 'Cache-Control'; + $this->computedCacheControl = $this->parseCacheControl($computed); + } + } + + /** + * {@inheritdoc} + */ + public function remove($key) + { + $uniqueKey = str_replace('_', '-', strtolower($key)); + unset($this->headerNames[$uniqueKey]); + + if ('set-cookie' === $uniqueKey) { + $this->cookies = array(); + + return; + } + + parent::remove($key); + + if ('cache-control' === $uniqueKey) { + $this->computedCacheControl = array(); + } + + if ('date' === $uniqueKey) { + $this->initDate(); + } + } + + /** + * {@inheritdoc} + */ + public function hasCacheControlDirective($key) + { + return array_key_exists($key, $this->computedCacheControl); + } + + /** + * {@inheritdoc} + */ + public function getCacheControlDirective($key) + { + return array_key_exists($key, $this->computedCacheControl) ? $this->computedCacheControl[$key] : null; + } + + public function setCookie(Cookie $cookie) + { + $this->cookies[$cookie->getDomain()][$cookie->getPath()][$cookie->getName()] = $cookie; + $this->headerNames['set-cookie'] = 'Set-Cookie'; + } + + /** + * Removes a cookie from the array, but does not unset it in the browser. + * + * @param string $name + * @param string $path + * @param string $domain + */ + public function removeCookie($name, $path = '/', $domain = null) + { + if (null === $path) { + $path = '/'; + } + + unset($this->cookies[$domain][$path][$name]); + + if (empty($this->cookies[$domain][$path])) { + unset($this->cookies[$domain][$path]); + + if (empty($this->cookies[$domain])) { + unset($this->cookies[$domain]); + } + } + + if (empty($this->cookies)) { + unset($this->headerNames['set-cookie']); + } + } + + /** + * Returns an array with all cookies. + * + * @param string $format + * + * @return array + * + * @throws \InvalidArgumentException When the $format is invalid + */ + 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 (self::COOKIES_ARRAY === $format) { + return $this->cookies; + } + + $flattenedCookies = array(); + foreach ($this->cookies as $path) { + foreach ($path as $cookies) { + foreach ($cookies as $cookie) { + $flattenedCookies[] = $cookie; + } + } + } + + return $flattenedCookies; + } + + /** + * Clears a cookie in the browser. + * + * @param string $name + * @param string $path + * @param string $domain + * @param bool $secure + * @param bool $httpOnly + */ + public function clearCookie($name, $path = '/', $domain = null, $secure = false, $httpOnly = true) + { + $this->setCookie(new Cookie($name, null, 1, $path, $domain, $secure, $httpOnly)); + } + + /** + * 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 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; + } + + /** + * Returns the calculated value of the cache-control header. + * + * This considers several other headers and calculates or modifies the + * cache-control header to a sensible, conservative value. + * + * @return string + */ + protected function computeCacheControlValue() + { + if (!$this->cacheControl && !$this->has('ETag') && !$this->has('Last-Modified') && !$this->has('Expires')) { + return 'no-cache, private'; + } + + if (!$this->cacheControl) { + // conservative by default + return 'private, must-revalidate'; + } + + $header = $this->getCacheControlHeader(); + if (isset($this->cacheControl['public']) || isset($this->cacheControl['private'])) { + return $header; + } + + // public if s-maxage is defined, private otherwise + if (!isset($this->cacheControl['s-maxage'])) { + return $header.', private'; + } + + return $header; + } + + private function initDate() + { + $now = \DateTime::createFromFormat('U', time()); + $now->setTimezone(new \DateTimeZone('UTC')); + $this->set('Date', $now->format('D, d M Y H:i:s').' GMT'); + } +} diff --git a/lib/silex/vendor/symfony/http-foundation/ServerBag.php b/lib/silex/vendor/symfony/http-foundation/ServerBag.php new file mode 100644 index 000000000..19d2022ef --- /dev/null +++ b/lib/silex/vendor/symfony/http-foundation/ServerBag.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; + +/** + * ServerBag is a container for HTTP headers from the $_SERVER variable. + * + * @author Fabien Potencier + * @author Bulat Shakirzyanov + * @author Robert Kiss + */ +class ServerBag extends ParameterBag +{ + /** + * Gets the HTTP headers. + * + * @return array + */ + public function getHeaders() + { + $headers = array(); + $contentHeaders = array('CONTENT_LENGTH' => true, 'CONTENT_MD5' => true, 'CONTENT_TYPE' => true); + 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])) { + $headers[$key] = $value; + } + } + + if (isset($this->parameters['PHP_AUTH_USER'])) { + $headers['PHP_AUTH_USER'] = $this->parameters['PHP_AUTH_USER']; + $headers['PHP_AUTH_PW'] = isset($this->parameters['PHP_AUTH_PW']) ? $this->parameters['PHP_AUTH_PW'] : ''; + } else { + /* + * 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}] + * + * A sample .htaccess file: + * RewriteEngine On + * RewriteCond %{HTTP:Authorization} ^(.+)$ + * RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}] + * RewriteCond %{REQUEST_FILENAME} !-f + * RewriteRule ^(.*)$ app.php [QSA,L] + */ + + $authorizationHeader = null; + if (isset($this->parameters['HTTP_AUTHORIZATION'])) { + $authorizationHeader = $this->parameters['HTTP_AUTHORIZATION']; + } elseif (isset($this->parameters['REDIRECT_HTTP_AUTHORIZATION'])) { + $authorizationHeader = $this->parameters['REDIRECT_HTTP_AUTHORIZATION']; + } + + if (null !== $authorizationHeader) { + if (0 === stripos($authorizationHeader, 'basic ')) { + // Decode AUTHORIZATION header into PHP_AUTH_USER and PHP_AUTH_PW when authorization header is basic + $exploded = explode(':', base64_decode(substr($authorizationHeader, 6)), 2); + if (2 == count($exploded)) { + list($headers['PHP_AUTH_USER'], $headers['PHP_AUTH_PW']) = $exploded; + } + } elseif (empty($this->parameters['PHP_AUTH_DIGEST']) && (0 === stripos($authorizationHeader, 'digest '))) { + // In some circumstances PHP_AUTH_DIGEST needs to be set + $headers['PHP_AUTH_DIGEST'] = $authorizationHeader; + $this->parameters['PHP_AUTH_DIGEST'] = $authorizationHeader; + } elseif (0 === stripos($authorizationHeader, 'bearer ')) { + /* + * 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 + */ + $headers['AUTHORIZATION'] = $authorizationHeader; + } + } + } + + if (isset($headers['AUTHORIZATION'])) { + return $headers; + } + + // PHP_AUTH_USER/PHP_AUTH_PW + if (isset($headers['PHP_AUTH_USER'])) { + $headers['AUTHORIZATION'] = 'Basic '.base64_encode($headers['PHP_AUTH_USER'].':'.$headers['PHP_AUTH_PW']); + } elseif (isset($headers['PHP_AUTH_DIGEST'])) { + $headers['AUTHORIZATION'] = $headers['PHP_AUTH_DIGEST']; + } + + return $headers; + } +} diff --git a/lib/silex/vendor/symfony/http-foundation/Session/Attribute/AttributeBag.php b/lib/silex/vendor/symfony/http-foundation/Session/Attribute/AttributeBag.php new file mode 100644 index 000000000..ea1fda290 --- /dev/null +++ b/lib/silex/vendor/symfony/http-foundation/Session/Attribute/AttributeBag.php @@ -0,0 +1,148 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\Session\Attribute; + +/** + * This class relates to session attribute storage. + */ +class AttributeBag implements AttributeBagInterface, \IteratorAggregate, \Countable +{ + private $name = 'attributes'; + private $storageKey; + + protected $attributes = array(); + + /** + * @param string $storageKey The key used to store attributes in the session + */ + public function __construct($storageKey = '_sf2_attributes') + { + $this->storageKey = $storageKey; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return $this->name; + } + + public function setName($name) + { + $this->name = $name; + } + + /** + * {@inheritdoc} + */ + public function initialize(array &$attributes) + { + $this->attributes = &$attributes; + } + + /** + * {@inheritdoc} + */ + public function getStorageKey() + { + return $this->storageKey; + } + + /** + * {@inheritdoc} + */ + public function has($name) + { + return array_key_exists($name, $this->attributes); + } + + /** + * {@inheritdoc} + */ + public function get($name, $default = null) + { + return array_key_exists($name, $this->attributes) ? $this->attributes[$name] : $default; + } + + /** + * {@inheritdoc} + */ + public function set($name, $value) + { + $this->attributes[$name] = $value; + } + + /** + * {@inheritdoc} + */ + public function all() + { + return $this->attributes; + } + + /** + * {@inheritdoc} + */ + public function replace(array $attributes) + { + $this->attributes = array(); + foreach ($attributes as $key => $value) { + $this->set($key, $value); + } + } + + /** + * {@inheritdoc} + */ + public function remove($name) + { + $retval = null; + if (array_key_exists($name, $this->attributes)) { + $retval = $this->attributes[$name]; + unset($this->attributes[$name]); + } + + return $retval; + } + + /** + * {@inheritdoc} + */ + public function clear() + { + $return = $this->attributes; + $this->attributes = array(); + + return $return; + } + + /** + * Returns an iterator for attributes. + * + * @return \ArrayIterator An \ArrayIterator instance + */ + public function getIterator() + { + return new \ArrayIterator($this->attributes); + } + + /** + * Returns the number of attributes. + * + * @return int The number of attributes + */ + public function count() + { + return count($this->attributes); + } +} diff --git a/lib/silex/vendor/symfony/http-foundation/Session/Attribute/AttributeBagInterface.php b/lib/silex/vendor/symfony/http-foundation/Session/Attribute/AttributeBagInterface.php new file mode 100644 index 000000000..0d8d17991 --- /dev/null +++ b/lib/silex/vendor/symfony/http-foundation/Session/Attribute/AttributeBagInterface.php @@ -0,0 +1,72 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\Session\Attribute; + +use Symfony\Component\HttpFoundation\Session\SessionBagInterface; + +/** + * Attributes store. + * + * @author Drak + */ +interface AttributeBagInterface extends SessionBagInterface +{ + /** + * Checks if an attribute is defined. + * + * @param string $name The attribute name + * + * @return bool true if the attribute is defined, false otherwise + */ + public function has($name); + + /** + * Returns an attribute. + * + * @param string $name The attribute name + * @param mixed $default The default value if not found + * + * @return mixed + */ + public function get($name, $default = null); + + /** + * Sets an attribute. + * + * @param string $name + * @param mixed $value + */ + public function set($name, $value); + + /** + * Returns attributes. + * + * @return array Attributes + */ + public function all(); + + /** + * Sets attributes. + * + * @param array $attributes Attributes + */ + public function replace(array $attributes); + + /** + * Removes an attribute. + * + * @param string $name + * + * @return mixed The removed value or null when it does not exist + */ + public function remove($name); +} diff --git a/lib/silex/vendor/symfony/http-foundation/Session/Attribute/NamespacedAttributeBag.php b/lib/silex/vendor/symfony/http-foundation/Session/Attribute/NamespacedAttributeBag.php new file mode 100644 index 000000000..abbf37ee7 --- /dev/null +++ b/lib/silex/vendor/symfony/http-foundation/Session/Attribute/NamespacedAttributeBag.php @@ -0,0 +1,153 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\Session\Attribute; + +/** + * This class provides structured storage of session attributes using + * a name spacing character in the key. + * + * @author Drak + */ +class NamespacedAttributeBag extends AttributeBag +{ + private $namespaceCharacter; + + /** + * @param string $storageKey Session storage key + * @param string $namespaceCharacter Namespace character to use in keys + */ + public function __construct($storageKey = '_sf2_attributes', $namespaceCharacter = '/') + { + $this->namespaceCharacter = $namespaceCharacter; + parent::__construct($storageKey); + } + + /** + * {@inheritdoc} + */ + public function has($name) + { + // reference mismatch: if fixed, re-introduced in array_key_exists; keep as it is + $attributes = $this->resolveAttributePath($name); + $name = $this->resolveKey($name); + + if (null === $attributes) { + return false; + } + + return array_key_exists($name, $attributes); + } + + /** + * {@inheritdoc} + */ + public function get($name, $default = null) + { + // reference mismatch: if fixed, re-introduced in array_key_exists; keep as it is + $attributes = $this->resolveAttributePath($name); + $name = $this->resolveKey($name); + + if (null === $attributes) { + return $default; + } + + return array_key_exists($name, $attributes) ? $attributes[$name] : $default; + } + + /** + * {@inheritdoc} + */ + public function set($name, $value) + { + $attributes = &$this->resolveAttributePath($name, true); + $name = $this->resolveKey($name); + $attributes[$name] = $value; + } + + /** + * {@inheritdoc} + */ + public function remove($name) + { + $retval = null; + $attributes = &$this->resolveAttributePath($name); + $name = $this->resolveKey($name); + if (null !== $attributes && array_key_exists($name, $attributes)) { + $retval = $attributes[$name]; + unset($attributes[$name]); + } + + return $retval; + } + + /** + * Resolves a path in attributes property and returns it as a reference. + * + * This method allows structured namespacing of session attributes. + * + * @param string $name Key name + * @param bool $writeContext Write context, default false + * + * @return array + */ + protected function &resolveAttributePath($name, $writeContext = false) + { + $array = &$this->attributes; + $name = (0 === strpos($name, $this->namespaceCharacter)) ? substr($name, 1) : $name; + + // Check if there is anything to do, else return + if (!$name) { + return $array; + } + + $parts = explode($this->namespaceCharacter, $name); + if (count($parts) < 2) { + if (!$writeContext) { + return $array; + } + + $array[$parts[0]] = array(); + + return $array; + } + + unset($parts[count($parts) - 1]); + + foreach ($parts as $part) { + if (null !== $array && !array_key_exists($part, $array)) { + $array[$part] = $writeContext ? array() : null; + } + + $array = &$array[$part]; + } + + return $array; + } + + /** + * Resolves the key from the name. + * + * This is the last part in a dot separated string. + * + * @param string $name + * + * @return string + */ + protected function resolveKey($name) + { + if (false !== $pos = strrpos($name, $this->namespaceCharacter)) { + $name = substr($name, $pos + 1); + } + + return $name; + } +} diff --git a/lib/silex/vendor/symfony/http-foundation/Session/Flash/AutoExpireFlashBag.php b/lib/silex/vendor/symfony/http-foundation/Session/Flash/AutoExpireFlashBag.php new file mode 100644 index 000000000..77521c247 --- /dev/null +++ b/lib/silex/vendor/symfony/http-foundation/Session/Flash/AutoExpireFlashBag.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\HttpFoundation\Session\Flash; + +/** + * AutoExpireFlashBag flash message container. + * + * @author Drak + */ +class AutoExpireFlashBag implements FlashBagInterface +{ + private $name = 'flashes'; + private $flashes = array('display' => array(), 'new' => array()); + private $storageKey; + + /** + * @param string $storageKey The key used to store flashes in the session + */ + public function __construct($storageKey = '_symfony_flashes') + { + $this->storageKey = $storageKey; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return $this->name; + } + + public function setName($name) + { + $this->name = $name; + } + + /** + * {@inheritdoc} + */ + public function initialize(array &$flashes) + { + $this->flashes = &$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(); + } + + /** + * {@inheritdoc} + */ + public function add($type, $message) + { + $this->flashes['new'][$type][] = $message; + } + + /** + * {@inheritdoc} + */ + public function peek($type, array $default = array()) + { + return $this->has($type) ? $this->flashes['display'][$type] : $default; + } + + /** + * {@inheritdoc} + */ + public function peekAll() + { + return array_key_exists('display', $this->flashes) ? (array) $this->flashes['display'] : array(); + } + + /** + * {@inheritdoc} + */ + public function get($type, array $default = array()) + { + $return = $default; + + if (!$this->has($type)) { + return $return; + } + + if (isset($this->flashes['display'][$type])) { + $return = $this->flashes['display'][$type]; + unset($this->flashes['display'][$type]); + } + + return $return; + } + + /** + * {@inheritdoc} + */ + public function all() + { + $return = $this->flashes['display']; + $this->flashes['display'] = array(); + + return $return; + } + + /** + * {@inheritdoc} + */ + public function setAll(array $messages) + { + $this->flashes['new'] = $messages; + } + + /** + * {@inheritdoc} + */ + public function set($type, $messages) + { + $this->flashes['new'][$type] = (array) $messages; + } + + /** + * {@inheritdoc} + */ + public function has($type) + { + return array_key_exists($type, $this->flashes['display']) && $this->flashes['display'][$type]; + } + + /** + * {@inheritdoc} + */ + public function keys() + { + return array_keys($this->flashes['display']); + } + + /** + * {@inheritdoc} + */ + public function getStorageKey() + { + return $this->storageKey; + } + + /** + * {@inheritdoc} + */ + public function clear() + { + return $this->all(); + } +} diff --git a/lib/silex/vendor/symfony/http-foundation/Session/Flash/FlashBag.php b/lib/silex/vendor/symfony/http-foundation/Session/Flash/FlashBag.php new file mode 100644 index 000000000..12fb740c5 --- /dev/null +++ b/lib/silex/vendor/symfony/http-foundation/Session/Flash/FlashBag.php @@ -0,0 +1,152 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\Session\Flash; + +/** + * FlashBag flash message container. + * + * @author Drak + */ +class FlashBag implements FlashBagInterface +{ + private $name = 'flashes'; + private $flashes = array(); + private $storageKey; + + /** + * @param string $storageKey The key used to store flashes in the session + */ + public function __construct($storageKey = '_symfony_flashes') + { + $this->storageKey = $storageKey; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return $this->name; + } + + public function setName($name) + { + $this->name = $name; + } + + /** + * {@inheritdoc} + */ + public function initialize(array &$flashes) + { + $this->flashes = &$flashes; + } + + /** + * {@inheritdoc} + */ + public function add($type, $message) + { + $this->flashes[$type][] = $message; + } + + /** + * {@inheritdoc} + */ + public function peek($type, array $default = array()) + { + return $this->has($type) ? $this->flashes[$type] : $default; + } + + /** + * {@inheritdoc} + */ + public function peekAll() + { + return $this->flashes; + } + + /** + * {@inheritdoc} + */ + public function get($type, array $default = array()) + { + if (!$this->has($type)) { + return $default; + } + + $return = $this->flashes[$type]; + + unset($this->flashes[$type]); + + return $return; + } + + /** + * {@inheritdoc} + */ + public function all() + { + $return = $this->peekAll(); + $this->flashes = array(); + + return $return; + } + + /** + * {@inheritdoc} + */ + public function set($type, $messages) + { + $this->flashes[$type] = (array) $messages; + } + + /** + * {@inheritdoc} + */ + public function setAll(array $messages) + { + $this->flashes = $messages; + } + + /** + * {@inheritdoc} + */ + public function has($type) + { + return array_key_exists($type, $this->flashes) && $this->flashes[$type]; + } + + /** + * {@inheritdoc} + */ + public function keys() + { + return array_keys($this->flashes); + } + + /** + * {@inheritdoc} + */ + public function getStorageKey() + { + return $this->storageKey; + } + + /** + * {@inheritdoc} + */ + public function clear() + { + return $this->all(); + } +} diff --git a/lib/silex/vendor/symfony/http-foundation/Session/Flash/FlashBagInterface.php b/lib/silex/vendor/symfony/http-foundation/Session/Flash/FlashBagInterface.php new file mode 100644 index 000000000..80e97f17c --- /dev/null +++ b/lib/silex/vendor/symfony/http-foundation/Session/Flash/FlashBagInterface.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\HttpFoundation\Session\Flash; + +use Symfony\Component\HttpFoundation\Session\SessionBagInterface; + +/** + * FlashBagInterface. + * + * @author Drak + */ +interface FlashBagInterface extends SessionBagInterface +{ + /** + * Adds a flash message for type. + * + * @param string $type + * @param string $message + */ + public function add($type, $message); + + /** + * Registers a message for a given type. + * + * @param string $type + * @param string|array $message + */ + public function set($type, $message); + + /** + * Gets flash messages for a given type. + * + * @param string $type Message category type + * @param array $default Default value if $type does not exist + * + * @return array + */ + public function peek($type, array $default = array()); + + /** + * Gets all flash messages. + * + * @return array + */ + public function peekAll(); + + /** + * Gets and clears flash from the stack. + * + * @param string $type + * @param array $default Default value if $type does not exist + * + * @return array + */ + public function get($type, array $default = array()); + + /** + * Gets and clears flashes from the stack. + * + * @return array + */ + public function all(); + + /** + * Sets all flash messages. + */ + public function setAll(array $messages); + + /** + * Has flash messages for a given type? + * + * @param string $type + * + * @return bool + */ + public function has($type); + + /** + * Returns a list of all defined types. + * + * @return array + */ + public function keys(); +} diff --git a/lib/silex/vendor/symfony/http-foundation/Session/Session.php b/lib/silex/vendor/symfony/http-foundation/Session/Session.php new file mode 100644 index 000000000..a46cffbb8 --- /dev/null +++ b/lib/silex/vendor/symfony/http-foundation/Session/Session.php @@ -0,0 +1,273 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\Session; + +use Symfony\Component\HttpFoundation\Session\Storage\SessionStorageInterface; +use Symfony\Component\HttpFoundation\Session\Attribute\AttributeBag; +use Symfony\Component\HttpFoundation\Session\Attribute\AttributeBagInterface; +use Symfony\Component\HttpFoundation\Session\Flash\FlashBag; +use Symfony\Component\HttpFoundation\Session\Flash\FlashBagInterface; +use Symfony\Component\HttpFoundation\Session\Storage\NativeSessionStorage; + +/** + * @author Fabien Potencier + * @author Drak + */ +class Session implements SessionInterface, \IteratorAggregate, \Countable +{ + protected $storage; + + private $flashName; + private $attributeName; + private $data = array(); + private $hasBeenStarted; + + /** + * @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(); + + $attributes = $attributes ?: new AttributeBag(); + $this->attributeName = $attributes->getName(); + $this->registerBag($attributes); + + $flashes = $flashes ?: new FlashBag(); + $this->flashName = $flashes->getName(); + $this->registerBag($flashes); + } + + /** + * {@inheritdoc} + */ + public function start() + { + return $this->storage->start(); + } + + /** + * {@inheritdoc} + */ + public function has($name) + { + return $this->getAttributeBag()->has($name); + } + + /** + * {@inheritdoc} + */ + public function get($name, $default = null) + { + return $this->getAttributeBag()->get($name, $default); + } + + /** + * {@inheritdoc} + */ + public function set($name, $value) + { + $this->getAttributeBag()->set($name, $value); + } + + /** + * {@inheritdoc} + */ + public function all() + { + return $this->getAttributeBag()->all(); + } + + /** + * {@inheritdoc} + */ + public function replace(array $attributes) + { + $this->getAttributeBag()->replace($attributes); + } + + /** + * {@inheritdoc} + */ + public function remove($name) + { + return $this->getAttributeBag()->remove($name); + } + + /** + * {@inheritdoc} + */ + public function clear() + { + $this->getAttributeBag()->clear(); + } + + /** + * {@inheritdoc} + */ + public function isStarted() + { + return $this->storage->isStarted(); + } + + /** + * Returns an iterator for attributes. + * + * @return \ArrayIterator An \ArrayIterator instance + */ + public function getIterator() + { + return new \ArrayIterator($this->getAttributeBag()->all()); + } + + /** + * Returns the number of attributes. + * + * @return int The number of attributes + */ + public function count() + { + return count($this->getAttributeBag()->all()); + } + + /** + * @return bool + * + * @internal + */ + public function hasBeenStarted() + { + return $this->hasBeenStarted; + } + + /** + * @return bool + * + * @internal + */ + public function isEmpty() + { + foreach ($this->data as &$data) { + if (!empty($data)) { + return false; + } + } + + return true; + } + + /** + * {@inheritdoc} + */ + public function invalidate($lifetime = null) + { + $this->storage->clear(); + + return $this->migrate(true, $lifetime); + } + + /** + * {@inheritdoc} + */ + public function migrate($destroy = false, $lifetime = null) + { + return $this->storage->regenerate($destroy, $lifetime); + } + + /** + * {@inheritdoc} + */ + public function save() + { + $this->storage->save(); + } + + /** + * {@inheritdoc} + */ + public function getId() + { + return $this->storage->getId(); + } + + /** + * {@inheritdoc} + */ + public function setId($id) + { + $this->storage->setId($id); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return $this->storage->getName(); + } + + /** + * {@inheritdoc} + */ + public function setName($name) + { + $this->storage->setName($name); + } + + /** + * {@inheritdoc} + */ + public function getMetadataBag() + { + return $this->storage->getMetadataBag(); + } + + /** + * {@inheritdoc} + */ + public function registerBag(SessionBagInterface $bag) + { + $this->storage->registerBag(new SessionBagProxy($bag, $this->data, $this->hasBeenStarted)); + } + + /** + * {@inheritdoc} + */ + public function getBag($name) + { + return $this->storage->getBag($name)->getBag(); + } + + /** + * Gets the flashbag interface. + * + * @return FlashBagInterface + */ + public function getFlashBag() + { + return $this->getBag($this->flashName); + } + + /** + * Gets the attributebag interface. + * + * Note that this method was added to help with IDE autocompletion. + * + * @return AttributeBagInterface + */ + private function getAttributeBag() + { + return $this->getBag($this->attributeName); + } +} diff --git a/lib/silex/vendor/symfony/http-foundation/Session/SessionBagInterface.php b/lib/silex/vendor/symfony/http-foundation/Session/SessionBagInterface.php new file mode 100644 index 000000000..8e37d06d6 --- /dev/null +++ b/lib/silex/vendor/symfony/http-foundation/Session/SessionBagInterface.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\HttpFoundation\Session; + +/** + * Session Bag store. + * + * @author Drak + */ +interface SessionBagInterface +{ + /** + * Gets this bag's name. + * + * @return string + */ + public function getName(); + + /** + * Initializes the Bag. + */ + public function initialize(array &$array); + + /** + * Gets the storage key for this bag. + * + * @return string + */ + public function getStorageKey(); + + /** + * Clears out data from bag. + * + * @return mixed Whatever data was contained + */ + public function clear(); +} diff --git a/lib/silex/vendor/symfony/http-foundation/Session/SessionBagProxy.php b/lib/silex/vendor/symfony/http-foundation/Session/SessionBagProxy.php new file mode 100644 index 000000000..307836d5f --- /dev/null +++ b/lib/silex/vendor/symfony/http-foundation/Session/SessionBagProxy.php @@ -0,0 +1,82 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\Session; + +/** + * @author Nicolas Grekas + * + * @internal + */ +final class SessionBagProxy implements SessionBagInterface +{ + private $bag; + private $data; + private $hasBeenStarted; + + public function __construct(SessionBagInterface $bag, array &$data, &$hasBeenStarted) + { + $this->bag = $bag; + $this->data = &$data; + $this->hasBeenStarted = &$hasBeenStarted; + } + + /** + * @return SessionBagInterface + */ + public function getBag() + { + return $this->bag; + } + + /** + * @return bool + */ + public function isEmpty() + { + return empty($this->data[$this->bag->getStorageKey()]); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return $this->bag->getName(); + } + + /** + * {@inheritdoc} + */ + public function initialize(array &$array) + { + $this->hasBeenStarted = true; + $this->data[$this->bag->getStorageKey()] = &$array; + + $this->bag->initialize($array); + } + + /** + * {@inheritdoc} + */ + public function getStorageKey() + { + return $this->bag->getStorageKey(); + } + + /** + * {@inheritdoc} + */ + public function clear() + { + return $this->bag->clear(); + } +} diff --git a/lib/silex/vendor/symfony/http-foundation/Session/SessionInterface.php b/lib/silex/vendor/symfony/http-foundation/Session/SessionInterface.php new file mode 100644 index 000000000..95fca857e --- /dev/null +++ b/lib/silex/vendor/symfony/http-foundation/Session/SessionInterface.php @@ -0,0 +1,180 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\Session; + +use Symfony\Component\HttpFoundation\Session\Storage\MetadataBag; + +/** + * Interface for the session. + * + * @author Drak + */ +interface SessionInterface +{ + /** + * Starts the session storage. + * + * @return bool True if session started + * + * @throws \RuntimeException if session fails to start + */ + public function start(); + + /** + * Returns the session ID. + * + * @return string The session ID + */ + public function getId(); + + /** + * Sets the session ID. + * + * @param string $id + */ + public function setId($id); + + /** + * Returns the session name. + * + * @return mixed The session name + */ + public function getName(); + + /** + * Sets the session name. + * + * @param string $name + */ + public function setName($name); + + /** + * Invalidates the current session. + * + * Clears all session attributes and flashes and regenerates the + * session and deletes the old session from persistence. + * + * @param int $lifetime Sets the cookie lifetime for the session cookie. A null value + * will leave the system settings unchanged, 0 sets the cookie + * to expire with browser session. Time is in seconds, and is + * not a Unix timestamp. + * + * @return bool True if session invalidated, false if error + */ + public function invalidate($lifetime = null); + + /** + * Migrates the current session to a new session id while maintaining all + * session attributes. + * + * @param bool $destroy Whether to delete the old session or leave it to garbage collection + * @param int $lifetime Sets the cookie lifetime for the session cookie. A null value + * will leave the system settings unchanged, 0 sets the cookie + * to expire with browser session. Time is in seconds, and is + * not a Unix timestamp. + * + * @return bool True if session migrated, false if error + */ + public function migrate($destroy = false, $lifetime = null); + + /** + * Force the session to be saved and closed. + * + * This method is generally not required for real sessions as + * the session will be automatically saved at the end of + * code execution. + */ + public function save(); + + /** + * Checks if an attribute is defined. + * + * @param string $name The attribute name + * + * @return bool true if the attribute is defined, false otherwise + */ + public function has($name); + + /** + * Returns an attribute. + * + * @param string $name The attribute name + * @param mixed $default The default value if not found + * + * @return mixed + */ + public function get($name, $default = null); + + /** + * Sets an attribute. + * + * @param string $name + * @param mixed $value + */ + public function set($name, $value); + + /** + * Returns attributes. + * + * @return array Attributes + */ + public function all(); + + /** + * Sets attributes. + * + * @param array $attributes Attributes + */ + public function replace(array $attributes); + + /** + * Removes an attribute. + * + * @param string $name + * + * @return mixed The removed value or null when it does not exist + */ + public function remove($name); + + /** + * Clears all attributes. + */ + public function clear(); + + /** + * Checks if the session was started. + * + * @return bool + */ + public function isStarted(); + + /** + * Registers a SessionBagInterface with the session. + */ + public function registerBag(SessionBagInterface $bag); + + /** + * Gets a bag instance by name. + * + * @param string $name + * + * @return SessionBagInterface + */ + public function getBag($name); + + /** + * Gets session meta. + * + * @return MetadataBag + */ + public function getMetadataBag(); +} diff --git a/lib/silex/vendor/symfony/http-foundation/Session/Storage/Handler/AbstractSessionHandler.php b/lib/silex/vendor/symfony/http-foundation/Session/Storage/Handler/AbstractSessionHandler.php new file mode 100644 index 000000000..6ae135581 --- /dev/null +++ b/lib/silex/vendor/symfony/http-foundation/Session/Storage/Handler/AbstractSessionHandler.php @@ -0,0 +1,168 @@ + + * + * 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; + +/** + * This abstract session handler provides a generic implementation + * of the PHP 7.0 SessionUpdateTimestampHandlerInterface, + * enabling strict and lazy session handling. + * + * @author Nicolas Grekas + */ +abstract class AbstractSessionHandler implements \SessionHandlerInterface, \SessionUpdateTimestampHandlerInterface +{ + private $sessionName; + private $prefetchId; + private $prefetchData; + private $newSessionId; + private $igbinaryEmptyData; + + /** + * {@inheritdoc} + */ + public function open($savePath, $sessionName) + { + $this->sessionName = $sessionName; + if (!headers_sent() && !ini_get('session.cache_limiter') && '0' !== ini_get('session.cache_limiter')) { + header(sprintf('Cache-Control: max-age=%d, private, must-revalidate', 60 * (int) ini_get('session.cache_expire'))); + } + + return true; + } + + /** + * @param string $sessionId + * + * @return string + */ + abstract protected function doRead($sessionId); + + /** + * @param string $sessionId + * @param string $data + * + * @return bool + */ + abstract protected function doWrite($sessionId, $data); + + /** + * @param string $sessionId + * + * @return bool + */ + abstract protected function doDestroy($sessionId); + + /** + * {@inheritdoc} + */ + public function validateId($sessionId) + { + $this->prefetchData = $this->read($sessionId); + $this->prefetchId = $sessionId; + + return '' !== $this->prefetchData; + } + + /** + * {@inheritdoc} + */ + public function read($sessionId) + { + if (null !== $this->prefetchId) { + $prefetchId = $this->prefetchId; + $prefetchData = $this->prefetchData; + $this->prefetchId = $this->prefetchData = null; + + if ($prefetchId === $sessionId || '' === $prefetchData) { + $this->newSessionId = '' === $prefetchData ? $sessionId : null; + + return $prefetchData; + } + } + + $data = $this->doRead($sessionId); + $this->newSessionId = '' === $data ? $sessionId : null; + if (\PHP_VERSION_ID < 70000) { + $this->prefetchData = $data; + } + + return $data; + } + + /** + * {@inheritdoc} + */ + 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()) : ''; + } + if ('' === $data || $this->igbinaryEmptyData === $data) { + return $this->destroy($sessionId); + } + $this->newSessionId = null; + + return $this->doWrite($sessionId, $data); + } + + /** + * {@inheritdoc} + */ + public function destroy($sessionId) + { + if (\PHP_VERSION_ID < 70000) { + $this->prefetchData = null; + } + if (!headers_sent() && ini_get('session.use_cookies')) { + if (!$this->sessionName) { + throw new \LogicException(sprintf('Session name cannot be empty, did you forget to call "parent::open()" in "%s"?.', get_class($this))); + } + $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; + } + } else { + $otherCookies[] = $h; + } + } + if ($sessionCookieFound) { + header_remove('Set-Cookie'); + foreach ($otherCookies as $h) { + header($h, false); + } + } else { + setcookie($this->sessionName, '', 0, ini_get('session.cookie_path'), ini_get('session.cookie_domain'), ini_get('session.cookie_secure'), ini_get('session.cookie_httponly')); + } + } + + return $this->newSessionId === $sessionId || $this->doDestroy($sessionId); + } +} diff --git a/lib/silex/vendor/symfony/http-foundation/Session/Storage/Handler/MemcacheSessionHandler.php b/lib/silex/vendor/symfony/http-foundation/Session/Storage/Handler/MemcacheSessionHandler.php new file mode 100644 index 000000000..90726beb0 --- /dev/null +++ b/lib/silex/vendor/symfony/http-foundation/Session/Storage/Handler/MemcacheSessionHandler.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; + +@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/lib/silex/vendor/symfony/http-foundation/Session/Storage/Handler/MemcachedSessionHandler.php b/lib/silex/vendor/symfony/http-foundation/Session/Storage/Handler/MemcachedSessionHandler.php new file mode 100644 index 000000000..9f4ef7028 --- /dev/null +++ b/lib/silex/vendor/symfony/http-foundation/Session/Storage/Handler/MemcachedSessionHandler.php @@ -0,0 +1,122 @@ + + * + * 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; + +/** + * Memcached based session storage handler based on the Memcached class + * provided by the PHP memcached extension. + * + * @see http://php.net/memcached + * + * @author Drak + */ +class MemcachedSessionHandler extends AbstractSessionHandler +{ + private $memcached; + + /** + * @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 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()) + { + $this->memcached = $memcached; + + if ($diff = array_diff(array_keys($options), array('prefix', 'expiretime'))) { + throw new \InvalidArgumentException(sprintf( + 'The following options are not supported "%s"', implode(', ', $diff) + )); + } + + $this->ttl = isset($options['expiretime']) ? (int) $options['expiretime'] : 86400; + $this->prefix = isset($options['prefix']) ? $options['prefix'] : 'sf2s'; + } + + /** + * {@inheritdoc} + */ + public function close() + { + return true; + } + + /** + * {@inheritdoc} + */ + protected function doRead($sessionId) + { + return $this->memcached->get($this->prefix.$sessionId) ?: ''; + } + + /** + * {@inheritdoc} + */ + public function updateTimestamp($sessionId, $data) + { + return $this->memcached->touch($this->prefix.$sessionId, time() + $this->ttl); + } + + /** + * {@inheritdoc} + */ + protected function doWrite($sessionId, $data) + { + return $this->memcached->set($this->prefix.$sessionId, $data, time() + $this->ttl); + } + + /** + * {@inheritdoc} + */ + protected function doDestroy($sessionId) + { + $result = $this->memcached->delete($this->prefix.$sessionId); + + return $result || \Memcached::RES_NOTFOUND == $this->memcached->getResultCode(); + } + + /** + * {@inheritdoc} + */ + public function gc($maxlifetime) + { + // not required here because memcached will auto expire the records anyhow. + return true; + } + + /** + * Return a Memcached instance. + * + * @return \Memcached + */ + protected function getMemcached() + { + return $this->memcached; + } +} diff --git a/lib/silex/vendor/symfony/http-foundation/Session/Storage/Handler/MongoDbSessionHandler.php b/lib/silex/vendor/symfony/http-foundation/Session/Storage/Handler/MongoDbSessionHandler.php new file mode 100644 index 000000000..7d3fa218a --- /dev/null +++ b/lib/silex/vendor/symfony/http-foundation/Session/Storage/Handler/MongoDbSessionHandler.php @@ -0,0 +1,255 @@ + + * + * 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; + +/** + * Session handler using the mongodb/mongodb package and MongoDB driver extension. + * + * @author Markus Bachmann + * + * @see https://packagist.org/packages/mongodb/mongodb + * @see http://php.net/manual/en/set.mongodb.php + */ +class MongoDbSessionHandler extends AbstractSessionHandler +{ + private $mongo; + + /** + * @var \MongoCollection + */ + private $collection; + + /** + * @var array + */ + private $options; + + /** + * Constructor. + * + * List of available options: + * * database: The name of the database [required] + * * collection: The name of the collection [required] + * * id_field: The field name for storing the session id [default: _id] + * * data_field: The field name for storing the session data [default: data] + * * time_field: The field name for storing the timestamp [default: time] + * * expiry_field: The field name for storing the expiry-timestamp [default: expires_at]. + * + * It is strongly recommended to put an index on the `expiry_field` for + * garbage-collection. Alternatively it's possible to automatically expire + * the sessions in the database as described below: + * + * A TTL collections can be used on MongoDB 2.2+ to cleanup expired sessions + * automatically. Such an index can for example look like this: + * + * db..ensureIndex( + * { "": 1 }, + * { "expireAfterSeconds": 0 } + * ) + * + * More details on: http://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) + { + 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( + 'id_field' => '_id', + 'data_field' => 'data', + 'time_field' => 'time', + 'expiry_field' => 'expires_at', + ), $options); + } + + /** + * {@inheritdoc} + */ + public function close() + { + return true; + } + + /** + * {@inheritdoc} + */ + protected function doDestroy($sessionId) + { + $methodName = $this->mongo instanceof \MongoDB\Client ? 'deleteOne' : 'remove'; + + $this->getCollection()->$methodName(array( + $this->options['id_field'] => $sessionId, + )); + + return true; + } + + /** + * {@inheritdoc} + */ + public function gc($maxlifetime) + { + $methodName = $this->mongo instanceof \MongoDB\Client ? 'deleteMany' : 'remove'; + + $this->getCollection()->$methodName(array( + $this->options['expiry_field'] => array('$lt' => $this->createDateTime()), + )); + + return true; + } + + /** + * {@inheritdoc} + */ + protected function doWrite($sessionId, $data) + { + $expiry = $this->createDateTime(time() + (int) ini_get('session.gc_maxlifetime')); + + $fields = array( + $this->options['time_field'] => $this->createDateTime(), + $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; + } + + $methodName = $this->mongo instanceof \MongoDB\Client ? 'updateOne' : 'update'; + + $this->getCollection()->$methodName( + array($this->options['id_field'] => $sessionId), + array('$set' => $fields), + $options + ); + + return true; + } + + /** + * {@inheritdoc} + */ + 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); + } + + $this->getCollection()->$methodName( + array($this->options['id_field'] => $sessionId), + array('$set' => array( + $this->options['time_field'] => $this->createDateTime(), + $this->options['expiry_field'] => $expiry, + )), + $options + ); + + return true; + } + + /** + * {@inheritdoc} + */ + protected function doRead($sessionId) + { + $dbData = $this->getCollection()->findOne(array( + $this->options['id_field'] => $sessionId, + $this->options['expiry_field'] => array('$gte' => $this->createDateTime()), + )); + + 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 a "MongoCollection" instance. + * + * @return \MongoCollection + */ + private function getCollection() + { + if (null === $this->collection) { + $this->collection = $this->mongo->selectCollection($this->options['database'], $this->options['collection']); + } + + return $this->collection; + } + + /** + * Return a Mongo instance. + * + * @return \Mongo|\MongoClient|\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/lib/silex/vendor/symfony/http-foundation/Session/Storage/Handler/NativeFileSessionHandler.php b/lib/silex/vendor/symfony/http-foundation/Session/Storage/Handler/NativeFileSessionHandler.php new file mode 100644 index 000000000..4e9704bd5 --- /dev/null +++ b/lib/silex/vendor/symfony/http-foundation/Session/Storage/Handler/NativeFileSessionHandler.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\Session\Storage\Handler; + +/** + * Native session handler using PHP's built in file storage. + * + * @author Drak + */ +class NativeFileSessionHandler extends NativeSessionHandler +{ + /** + * @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. + * + * @throws \InvalidArgumentException On invalid $savePath + * @throws \RuntimeException When failing to create the save directory + */ + public function __construct($savePath = null) + { + if (null === $savePath) { + $savePath = ini_get('session.save_path'); + } + + $baseDir = $savePath; + + if ($count = substr_count($savePath, ';')) { + if ($count > 2) { + throw new \InvalidArgumentException(sprintf('Invalid argument $savePath \'%s\'', $savePath)); + } + + // characters after last ';' are the path + $baseDir = ltrim(strrchr($savePath, ';'), ';'); + } + + if ($baseDir && !is_dir($baseDir) && !@mkdir($baseDir, 0777, true) && !is_dir($baseDir)) { + throw new \RuntimeException(sprintf('Session Storage was not able to create directory "%s"', $baseDir)); + } + + ini_set('session.save_path', $savePath); + ini_set('session.save_handler', 'files'); + } +} diff --git a/lib/silex/vendor/symfony/http-foundation/Session/Storage/Handler/NativeSessionHandler.php b/lib/silex/vendor/symfony/http-foundation/Session/Storage/Handler/NativeSessionHandler.php new file mode 100644 index 000000000..9be4528ae --- /dev/null +++ b/lib/silex/vendor/symfony/http-foundation/Session/Storage/Handler/NativeSessionHandler.php @@ -0,0 +1,24 @@ + + * + * 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/lib/silex/vendor/symfony/http-foundation/Session/Storage/Handler/NullSessionHandler.php b/lib/silex/vendor/symfony/http-foundation/Session/Storage/Handler/NullSessionHandler.php new file mode 100644 index 000000000..8d193155b --- /dev/null +++ b/lib/silex/vendor/symfony/http-foundation/Session/Storage/Handler/NullSessionHandler.php @@ -0,0 +1,76 @@ + + * + * 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; + +/** + * Can be used in unit testing or in a situations where persisted sessions are not desired. + * + * @author Drak + */ +class NullSessionHandler extends AbstractSessionHandler +{ + /** + * {@inheritdoc} + */ + public function close() + { + return true; + } + + /** + * {@inheritdoc} + */ + public function validateId($sessionId) + { + return true; + } + + /** + * {@inheritdoc} + */ + protected function doRead($sessionId) + { + return ''; + } + + /** + * {@inheritdoc} + */ + public function updateTimestamp($sessionId, $data) + { + return true; + } + + /** + * {@inheritdoc} + */ + protected function doWrite($sessionId, $data) + { + return true; + } + + /** + * {@inheritdoc} + */ + protected function doDestroy($sessionId) + { + return true; + } + + /** + * {@inheritdoc} + */ + public function gc($maxlifetime) + { + return true; + } +} diff --git a/lib/silex/vendor/symfony/http-foundation/Session/Storage/Handler/PdoSessionHandler.php b/lib/silex/vendor/symfony/http-foundation/Session/Storage/Handler/PdoSessionHandler.php new file mode 100644 index 000000000..2e1692b6f --- /dev/null +++ b/lib/silex/vendor/symfony/http-foundation/Session/Storage/Handler/PdoSessionHandler.php @@ -0,0 +1,812 @@ + + * + * 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; + +/** + * Session handler using a PDO connection to read and write data. + * + * It works with MySQL, PostgreSQL, Oracle, SQL Server and SQLite and implements + * different locking strategies to handle concurrent access to the same session. + * Locking is necessary to prevent loss of data due to race conditions and to keep + * the session data consistent between read() and write(). With locking, requests + * for the same session will wait until the other one finished writing. For this + * reason it's best practice to close a session as early as possible to improve + * concurrency. PHPs internal files session handler also implements locking. + * + * Attention: Since SQLite does not support row level locks but locks the whole database, + * it means only one session can be accessed at a time. Even different sessions would wait + * for another to finish. So saving session in SQLite should only be considered for + * development or prototypes. + * + * Session data is a binary string that can contain non-printable characters like the null byte. + * For this reason it must be saved in a binary column in the database like BLOB in MySQL. + * 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 + * + * @author Fabien Potencier + * @author Michael Williams + * @author Tobias Schultze + */ +class PdoSessionHandler extends AbstractSessionHandler +{ + /** + * No locking is done. This means sessions are prone to loss of data due to + * race conditions of concurrent requests to the same session. The last session + * write will win in this case. It might be useful when you implement your own + * logic to deal with this like an optimistic approach. + */ + const LOCK_NONE = 0; + + /** + * Creates an application-level lock on a session. The disadvantage is that the + * lock is not enforced by the database and thus other, unaware parts of the + * application could still concurrently modify the session. The advantage is it + * does not require a transaction. + * This mode is not available for SQLite and not yet implemented for oci and sqlsrv. + */ + const LOCK_ADVISORY = 1; + + /** + * Issues a real row lock. Since it uses a transaction between opening and + * closing a session, you have to be careful when you use same database connection + * that you also use for your application logic. This mode is the default because + * it's the only reliable solution across DBMSs. + */ + const LOCK_TRANSACTIONAL = 2; + + /** + * @var \PDO|null PDO instance or null when not connected yet + */ + private $pdo; + + /** + * @var string|null|false DSN string or null for session.save_path or false when lazy connection disabled + */ + private $dsn = false; + + /** + * @var string Database driver + */ + private $driver; + + /** + * @var string Table name + */ + private $table = 'sessions'; + + /** + * @var string Column for session id + */ + private $idCol = 'sess_id'; + + /** + * @var string Column for session data + */ + private $dataCol = 'sess_data'; + + /** + * @var string Column for lifetime + */ + private $lifetimeCol = 'sess_lifetime'; + + /** + * @var string Column for timestamp + */ + private $timeCol = 'sess_time'; + + /** + * @var string Username when lazy-connect + */ + private $username = ''; + + /** + * @var string Password when lazy-connect + */ + private $password = ''; + + /** + * @var array Connection options when lazy-connect + */ + private $connectionOptions = array(); + + /** + * @var int The strategy for locking, see constants + */ + private $lockMode = self::LOCK_TRANSACTIONAL; + + /** + * It's an array to support multiple reads before closing which is manual, non-standard usage. + * + * @var \PDOStatement[] An array of statements to release advisory locks + */ + private $unlockStatements = array(); + + /** + * @var bool True when the current session exists but expired according to session.gc_maxlifetime + */ + private $sessionExpired = false; + + /** + * @var bool Whether a transaction is active + */ + private $inTransaction = false; + + /** + * @var bool Whether gc() has been called + */ + private $gcCalled = false; + + /** + * You can either pass an existing database connection as PDO instance or + * pass a DSN string that will be used to lazy-connect to the database + * when the session is actually used. Furthermore it's possible to pass null + * which will then use the session.save_path ini setting as PDO DSN parameter. + * + * List of available options: + * * db_table: The name of the table [default: sessions] + * * db_id_col: The column where to store the session id [default: sess_id] + * * db_data_col: The column where to store the session data [default: sess_data] + * * db_lifetime_col: The column where to store the lifetime [default: sess_lifetime] + * * 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()] + * * lock_mode: The strategy for locking, see constants [default: LOCK_TRANSACTIONAL] + * + * @param \PDO|string|null $pdoOrDsn A \PDO instance or DSN 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()) + { + if ($pdoOrDsn instanceof \PDO) { + if (\PDO::ERRMODE_EXCEPTION !== $pdoOrDsn->getAttribute(\PDO::ATTR_ERRMODE)) { + throw new \InvalidArgumentException(sprintf('"%s" requires PDO error mode attribute be set to throw Exceptions (i.e. $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION))', __CLASS__)); + } + + $this->pdo = $pdoOrDsn; + $this->driver = $this->pdo->getAttribute(\PDO::ATTR_DRIVER_NAME); + } else { + $this->dsn = $pdoOrDsn; + } + + $this->table = isset($options['db_table']) ? $options['db_table'] : $this->table; + $this->idCol = isset($options['db_id_col']) ? $options['db_id_col'] : $this->idCol; + $this->dataCol = isset($options['db_data_col']) ? $options['db_data_col'] : $this->dataCol; + $this->lifetimeCol = isset($options['db_lifetime_col']) ? $options['db_lifetime_col'] : $this->lifetimeCol; + $this->timeCol = isset($options['db_time_col']) ? $options['db_time_col'] : $this->timeCol; + $this->username = isset($options['db_username']) ? $options['db_username'] : $this->username; + $this->password = isset($options['db_password']) ? $options['db_password'] : $this->password; + $this->connectionOptions = isset($options['db_connection_options']) ? $options['db_connection_options'] : $this->connectionOptions; + $this->lockMode = isset($options['lock_mode']) ? $options['lock_mode'] : $this->lockMode; + } + + /** + * Creates the table to store sessions which can be called once for setup. + * + * Session ID is saved in a column of maximum length 128 because that is enough even + * for a 512 bit configured session.hash_function like Whirlpool. Session data is + * saved in a BLOB. One could also use a shorter inlined varbinary column + * if one was sure the data fits into it. + * + * @throws \PDOException When the table already exists + * @throws \DomainException When an unsupported PDO driver is used + */ + public function createTable() + { + // connect if we are not yet + $this->getConnection(); + + switch ($this->driver) { + case 'mysql': + // We use varbinary for the ID column because it prevents unwanted conversions: + // - character set conversions between server and client + // - 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"; + 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)"; + break; + case 'pgsql': + $sql = "CREATE TABLE $this->table ($this->idCol VARCHAR(128) NOT NULL PRIMARY KEY, $this->dataCol BYTEA NOT NULL, $this->lifetimeCol INTEGER NOT NULL, $this->timeCol INTEGER NOT NULL)"; + break; + case 'oci': + $sql = "CREATE TABLE $this->table ($this->idCol VARCHAR2(128) NOT NULL PRIMARY KEY, $this->dataCol BLOB NOT NULL, $this->lifetimeCol INTEGER NOT NULL, $this->timeCol INTEGER NOT NULL)"; + break; + case 'sqlsrv': + $sql = "CREATE TABLE $this->table ($this->idCol VARCHAR(128) NOT NULL PRIMARY KEY, $this->dataCol VARBINARY(MAX) NOT NULL, $this->lifetimeCol INTEGER NOT NULL, $this->timeCol INTEGER NOT NULL)"; + break; + default: + throw new \DomainException(sprintf('Creating the session table is currently not implemented for PDO driver "%s".', $this->driver)); + } + + try { + $this->pdo->exec($sql); + } catch (\PDOException $e) { + $this->rollback(); + + throw $e; + } + } + + /** + * Returns true when the current session exists but expired according to session.gc_maxlifetime. + * + * Can be used to distinguish between a new session and one that expired due to inactivity. + * + * @return bool Whether current session expired + */ + public function isSessionExpired() + { + return $this->sessionExpired; + } + + /** + * {@inheritdoc} + */ + public function open($savePath, $sessionName) + { + $this->sessionExpired = false; + + if (null === $this->pdo) { + $this->connect($this->dsn ?: $savePath); + } + + return parent::open($savePath, $sessionName); + } + + /** + * {@inheritdoc} + */ + public function read($sessionId) + { + try { + return parent::read($sessionId); + } catch (\PDOException $e) { + $this->rollback(); + + throw $e; + } + } + + /** + * {@inheritdoc} + */ + public function gc($maxlifetime) + { + // We delay gc() to close() so that it is executed outside the transactional and blocking read-write process. + // This way, pruning expired sessions does not block them from being started while the current session is used. + $this->gcCalled = true; + + return true; + } + + /** + * {@inheritdoc} + */ + protected function doDestroy($sessionId) + { + // delete the record associated with this id + $sql = "DELETE FROM $this->table WHERE $this->idCol = :id"; + + try { + $stmt = $this->pdo->prepare($sql); + $stmt->bindParam(':id', $sessionId, \PDO::PARAM_STR); + $stmt->execute(); + } catch (\PDOException $e) { + $this->rollback(); + + throw $e; + } + + return true; + } + + /** + * {@inheritdoc} + */ + protected function doWrite($sessionId, $data) + { + $maxlifetime = (int) ini_get('session.gc_maxlifetime'); + + try { + // We use a single MERGE SQL query when supported by the database. + $mergeStmt = $this->getMergeStatement($sessionId, $data, $maxlifetime); + if (null !== $mergeStmt) { + $mergeStmt->execute(); + + return true; + } + + $updateStmt = $this->getUpdateStatement($sessionId, $data, $maxlifetime); + $updateStmt->execute(); + + // When MERGE is not supported, like in Postgres < 9.5, we have to use this approach that can result in + // duplicate key errors when the same session is written simultaneously (given the LOCK_NONE behavior). + // We can just catch such an error and re-execute the update. This is similar to a serializable + // transaction with retry logic on serialization failures but without the overhead and without possible + // false positives due to longer gap locking. + if (!$updateStmt->rowCount()) { + try { + $insertStmt = $this->getInsertStatement($sessionId, $data, $maxlifetime); + $insertStmt->execute(); + } catch (\PDOException $e) { + // Handle integrity violation SQLSTATE 23000 (or a subclass like 23505 in Postgres) for duplicate keys + if (0 === strpos($e->getCode(), '23')) { + $updateStmt->execute(); + } else { + throw $e; + } + } + } + } catch (\PDOException $e) { + $this->rollback(); + + throw $e; + } + + return true; + } + + /** + * {@inheritdoc} + */ + public function updateTimestamp($sessionId, $data) + { + $maxlifetime = (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" + ); + $updateStmt->bindParam(':id', $sessionId, \PDO::PARAM_STR); + $updateStmt->bindParam(':lifetime', $maxlifetime, \PDO::PARAM_INT); + $updateStmt->bindValue(':time', time(), \PDO::PARAM_INT); + $updateStmt->execute(); + } catch (\PDOException $e) { + $this->rollback(); + + throw $e; + } + + return true; + } + + /** + * {@inheritdoc} + */ + public function close() + { + $this->commit(); + + while ($unlockStmt = array_shift($this->unlockStatements)) { + $unlockStmt->execute(); + } + + if ($this->gcCalled) { + $this->gcCalled = false; + + // delete the session records that have expired + if ('mysql' === $this->driver) { + $sql = "DELETE FROM $this->table WHERE $this->lifetimeCol + $this->timeCol < :time"; + } else { + $sql = "DELETE FROM $this->table WHERE $this->lifetimeCol < :time - $this->timeCol"; + } + + $stmt = $this->pdo->prepare($sql); + $stmt->bindValue(':time', time(), \PDO::PARAM_INT); + $stmt->execute(); + } + + if (false !== $this->dsn) { + $this->pdo = null; // only close lazy-connection + } + + return true; + } + + /** + * Lazy-connects to the database. + * + * @param string $dsn DSN string + */ + private function connect($dsn) + { + $this->pdo = new \PDO($dsn, $this->username, $this->password, $this->connectionOptions); + $this->pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION); + $this->driver = $this->pdo->getAttribute(\PDO::ATTR_DRIVER_NAME); + } + + /** + * Helper method to begin a transaction. + * + * Since SQLite does not support row level locks, we have to acquire a reserved lock + * on the database immediately. Because of https://bugs.php.net/42766 we have to create + * such a transaction manually which also means we cannot use PDO::commit or + * 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/ . + * So we change it to READ COMMITTED. + */ + private function beginTransaction() + { + if (!$this->inTransaction) { + if ('sqlite' === $this->driver) { + $this->pdo->exec('BEGIN IMMEDIATE TRANSACTION'); + } else { + if ('mysql' === $this->driver) { + $this->pdo->exec('SET TRANSACTION ISOLATION LEVEL READ COMMITTED'); + } + $this->pdo->beginTransaction(); + } + $this->inTransaction = true; + } + } + + /** + * Helper method to commit a transaction. + */ + private function commit() + { + if ($this->inTransaction) { + try { + // commit read-write transaction which also releases the lock + if ('sqlite' === $this->driver) { + $this->pdo->exec('COMMIT'); + } else { + $this->pdo->commit(); + } + $this->inTransaction = false; + } catch (\PDOException $e) { + $this->rollback(); + + throw $e; + } + } + } + + /** + * Helper method to rollback a transaction. + */ + private function rollback() + { + // 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 + // in a transaction when not using the transactional locking behavior or when + // two callbacks (e.g. destroy and write) are invoked that both fail. + if ($this->inTransaction) { + if ('sqlite' === $this->driver) { + $this->pdo->exec('ROLLBACK'); + } else { + $this->pdo->rollBack(); + } + $this->inTransaction = false; + } + } + + /** + * Reads the session data in respect to the different locking strategies. + * + * We need to make sure we do not return session data that is already considered garbage according + * to the session.gc_maxlifetime setting because gc() is called after read() and only sometimes. + * + * @param string $sessionId Session ID + * + * @return string The session data + */ + protected function doRead($sessionId) + { + if (self::LOCK_ADVISORY === $this->lockMode) { + $this->unlockStatements[] = $this->doAdvisoryLock($sessionId); + } + + $selectSql = $this->getSelectSql(); + $selectStmt = $this->pdo->prepare($selectSql); + $selectStmt->bindParam(':id', $sessionId, \PDO::PARAM_STR); + + do { + $selectStmt->execute(); + $sessionRows = $selectStmt->fetchAll(\PDO::FETCH_NUM); + + if ($sessionRows) { + if ($sessionRows[0][1] + $sessionRows[0][2] < time()) { + $this->sessionExpired = true; + + return ''; + } + + return is_resource($sessionRows[0][0]) ? stream_get_contents($sessionRows[0][0]) : $sessionRows[0][0]; + } + + if (!ini_get('session.use_strict_mode') && self::LOCK_TRANSACTIONAL === $this->lockMode && 'sqlite' !== $this->driver) { + // In strict mode, session fixation is not possible: new sessions always start with a unique + // random id, so that concurrency is not possible and this code path can be skipped. + // Exclusive-reading of non-existent rows does not block, so we need to do an insert to block + // until other connections to the session are committed. + try { + $insertStmt = $this->getInsertStatement($sessionId, '', 0); + $insertStmt->execute(); + } catch (\PDOException $e) { + // Catch duplicate key error because other connection created the session already. + // It would only not be the case when the other connection destroyed the session. + if (0 === strpos($e->getCode(), '23')) { + // Retrieve finished session data written by concurrent connection by restarting the loop. + // We have to start a new transaction as a failed query will mark the current transaction as + // aborted in PostgreSQL and disallow further queries within it. + $this->rollback(); + $this->beginTransaction(); + continue; + } + + throw $e; + } + } + + return ''; + } while (true); + } + + /** + * 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 + * + * @todo implement missing advisory locks + * - for oci using DBMS_LOCK.REQUEST + * - for sqlsrv using sp_getapplock with LockOwner = Session + */ + private function doAdvisoryLock($sessionId) + { + switch ($this->driver) { + case 'mysql': + // 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)'); + $stmt->bindValue(':key', $sessionId, \PDO::PARAM_STR); + $stmt->execute(); + + $releaseStmt = $this->pdo->prepare('DO RELEASE_LOCK(:key)'); + $releaseStmt->bindValue(':key', $sessionId, \PDO::PARAM_STR); + + return $releaseStmt; + case 'pgsql': + // Obtaining an exclusive session level advisory lock requires an integer key. + // When session.sid_bits_per_character > 4, the session id can contain non-hex-characters. + // So we cannot just use hexdec(). + if (4 === \PHP_INT_SIZE) { + $sessionInt1 = $this->convertStringToInt($sessionId); + $sessionInt2 = $this->convertStringToInt(substr($sessionId, 4, 4)); + + $stmt = $this->pdo->prepare('SELECT pg_advisory_lock(:key1, :key2)'); + $stmt->bindValue(':key1', $sessionInt1, \PDO::PARAM_INT); + $stmt->bindValue(':key2', $sessionInt2, \PDO::PARAM_INT); + $stmt->execute(); + + $releaseStmt = $this->pdo->prepare('SELECT pg_advisory_unlock(:key1, :key2)'); + $releaseStmt->bindValue(':key1', $sessionInt1, \PDO::PARAM_INT); + $releaseStmt->bindValue(':key2', $sessionInt2, \PDO::PARAM_INT); + } else { + $sessionBigInt = $this->convertStringToInt($sessionId); + + $stmt = $this->pdo->prepare('SELECT pg_advisory_lock(:key)'); + $stmt->bindValue(':key', $sessionBigInt, \PDO::PARAM_INT); + $stmt->execute(); + + $releaseStmt = $this->pdo->prepare('SELECT pg_advisory_unlock(:key)'); + $releaseStmt->bindValue(':key', $sessionBigInt, \PDO::PARAM_INT); + } + + return $releaseStmt; + case 'sqlite': + throw new \DomainException('SQLite does not support advisory locks.'); + default: + throw new \DomainException(sprintf('Advisory locks are currently not implemented for PDO driver "%s".', $this->driver)); + } + } + + /** + * 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) + { + if (4 === \PHP_INT_SIZE) { + return (ord($string[3]) << 24) + (ord($string[2]) << 16) + (ord($string[1]) << 8) + ord($string[0]); + } + + $int1 = (ord($string[7]) << 24) + (ord($string[6]) << 16) + (ord($string[5]) << 8) + ord($string[4]); + $int2 = (ord($string[3]) << 24) + (ord($string[2]) << 16) + (ord($string[1]) << 8) + ord($string[0]); + + return $int2 + ($int1 << 32); + } + + /** + * 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() + { + if (self::LOCK_TRANSACTIONAL === $this->lockMode) { + $this->beginTransaction(); + + switch ($this->driver) { + case 'mysql': + case 'oci': + case 'pgsql': + return "SELECT $this->dataCol, $this->lifetimeCol, $this->timeCol FROM $this->table WHERE $this->idCol = :id FOR UPDATE"; + case 'sqlsrv': + return "SELECT $this->dataCol, $this->lifetimeCol, $this->timeCol FROM $this->table WITH (UPDLOCK, ROWLOCK) WHERE $this->idCol = :id"; + case 'sqlite': + // we already locked when starting transaction + break; + default: + throw new \DomainException(sprintf('Transactional locks are currently not implemented for PDO driver "%s".', $this->driver)); + } + } + + return "SELECT $this->dataCol, $this->lifetimeCol, $this->timeCol FROM $this->table WHERE $this->idCol = :id"; + } + + /** + * 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) + { + 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"; + break; + default: + $data = $sessionData; + $sql = "INSERT INTO $this->table ($this->idCol, $this->dataCol, $this->lifetimeCol, $this->timeCol) VALUES (:id, :data, :lifetime, :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(':time', time(), \PDO::PARAM_INT); + + return $stmt; + } + + /** + * 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) + { + 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"; + break; + default: + $data = $sessionData; + $sql = "UPDATE $this->table SET $this->dataCol = :data, $this->lifetimeCol = :lifetime, $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(':time', time(), \PDO::PARAM_INT); + + return $stmt; + } + + /** + * 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) + { + switch (true) { + case 'mysql' === $this->driver: + $mergeSql = "INSERT INTO $this->table ($this->idCol, $this->dataCol, $this->lifetimeCol, $this->timeCol) VALUES (:id, :data, :lifetime, :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 + $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)"; + 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) ". + "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 + return null; + } + + $mergeStmt = $this->pdo->prepare($mergeSql); + + if ('sqlsrv' === $this->driver) { + $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(5, time(), \PDO::PARAM_INT); + $mergeStmt->bindParam(6, $data, \PDO::PARAM_LOB); + $mergeStmt->bindParam(7, $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(':time', time(), \PDO::PARAM_INT); + } + + return $mergeStmt; + } + + /** + * Return a PDO instance. + * + * @return \PDO + */ + protected function getConnection() + { + if (null === $this->pdo) { + $this->connect($this->dsn ?: ini_get('session.save_path')); + } + + return $this->pdo; + } +} diff --git a/lib/silex/vendor/symfony/http-foundation/Session/Storage/Handler/StrictSessionHandler.php b/lib/silex/vendor/symfony/http-foundation/Session/Storage/Handler/StrictSessionHandler.php new file mode 100644 index 000000000..228119297 --- /dev/null +++ b/lib/silex/vendor/symfony/http-foundation/Session/Storage/Handler/StrictSessionHandler.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\HttpFoundation\Session\Storage\Handler; + +/** + * Adds basic `SessionUpdateTimestampHandlerInterface` behaviors to another `SessionHandlerInterface`. + * + * @author Nicolas Grekas + */ +class StrictSessionHandler extends AbstractSessionHandler +{ + private $handler; + private $doDestroy; + + public function __construct(\SessionHandlerInterface $handler) + { + if ($handler instanceof \SessionUpdateTimestampHandlerInterface) { + throw new \LogicException(sprintf('"%s" is already an instance of "SessionUpdateTimestampHandlerInterface", you cannot wrap it with "%s".', get_class($handler), self::class)); + } + + $this->handler = $handler; + } + + /** + * {@inheritdoc} + */ + public function open($savePath, $sessionName) + { + parent::open($savePath, $sessionName); + + return $this->handler->open($savePath, $sessionName); + } + + /** + * {@inheritdoc} + */ + protected function doRead($sessionId) + { + return $this->handler->read($sessionId); + } + + /** + * {@inheritdoc} + */ + public function updateTimestamp($sessionId, $data) + { + return $this->write($sessionId, $data); + } + + /** + * {@inheritdoc} + */ + protected function doWrite($sessionId, $data) + { + return $this->handler->write($sessionId, $data); + } + + /** + * {@inheritdoc} + */ + public function destroy($sessionId) + { + $this->doDestroy = true; + $destroyed = parent::destroy($sessionId); + + return $this->doDestroy ? $this->doDestroy($sessionId) : $destroyed; + } + + /** + * {@inheritdoc} + */ + protected function doDestroy($sessionId) + { + $this->doDestroy = false; + + return $this->handler->destroy($sessionId); + } + + /** + * {@inheritdoc} + */ + public function close() + { + return $this->handler->close(); + } + + /** + * {@inheritdoc} + */ + public function gc($maxlifetime) + { + return $this->handler->gc($maxlifetime); + } +} diff --git a/lib/silex/vendor/symfony/http-foundation/Session/Storage/Handler/WriteCheckSessionHandler.php b/lib/silex/vendor/symfony/http-foundation/Session/Storage/Handler/WriteCheckSessionHandler.php new file mode 100644 index 000000000..1541ec4e0 --- /dev/null +++ b/lib/silex/vendor/symfony/http-foundation/Session/Storage/Handler/WriteCheckSessionHandler.php @@ -0,0 +1,92 @@ + + * + * 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 %s class is deprecated since Symfony 3.4 and will be removed in 4.0. Implement `SessionUpdateTimestampHandlerInterface` or extend `AbstractSessionHandler` instead.', WriteCheckSessionHandler::class), E_USER_DEPRECATED); + +/** + * 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) + { + $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/lib/silex/vendor/symfony/http-foundation/Session/Storage/MetadataBag.php b/lib/silex/vendor/symfony/http-foundation/Session/Storage/MetadataBag.php new file mode 100644 index 000000000..6f59af486 --- /dev/null +++ b/lib/silex/vendor/symfony/http-foundation/Session/Storage/MetadataBag.php @@ -0,0 +1,168 @@ + + * + * 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; + +use Symfony\Component\HttpFoundation\Session\SessionBagInterface; + +/** + * Metadata container. + * + * Adds metadata to the session. + * + * @author Drak + */ +class MetadataBag implements SessionBagInterface +{ + const CREATED = 'c'; + const UPDATED = 'u'; + const LIFETIME = 'l'; + + /** + * @var string + */ + private $name = '__metadata'; + + /** + * @var string + */ + private $storageKey; + + /** + * @var array + */ + protected $meta = array(self::CREATED => 0, self::UPDATED => 0, self::LIFETIME => 0); + + /** + * Unix timestamp. + * + * @var int + */ + private $lastUsed; + + /** + * @var int + */ + private $updateThreshold; + + /** + * @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) + { + $this->storageKey = $storageKey; + $this->updateThreshold = $updateThreshold; + } + + /** + * {@inheritdoc} + */ + public function initialize(array &$array) + { + $this->meta = &$array; + + if (isset($array[self::CREATED])) { + $this->lastUsed = $this->meta[self::UPDATED]; + + $timeStamp = time(); + if ($timeStamp - $array[self::UPDATED] >= $this->updateThreshold) { + $this->meta[self::UPDATED] = $timeStamp; + } + } else { + $this->stampCreated(); + } + } + + /** + * Gets the lifetime that the session cookie was set with. + * + * @return int + */ + public function getLifetime() + { + return $this->meta[self::LIFETIME]; + } + + /** + * Stamps a new session's metadata. + * + * @param int $lifetime Sets the cookie lifetime for the session cookie. A null value + * will leave the system settings unchanged, 0 sets the cookie + * to expire with browser session. Time is in seconds, and is + * not a Unix timestamp. + */ + public function stampNew($lifetime = null) + { + $this->stampCreated($lifetime); + } + + /** + * {@inheritdoc} + */ + public function getStorageKey() + { + return $this->storageKey; + } + + /** + * Gets the created timestamp metadata. + * + * @return int Unix timestamp + */ + public function getCreated() + { + return $this->meta[self::CREATED]; + } + + /** + * Gets the last used metadata. + * + * @return int Unix timestamp + */ + public function getLastUsed() + { + return $this->lastUsed; + } + + /** + * {@inheritdoc} + */ + public function clear() + { + // nothing to do + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return $this->name; + } + + /** + * Sets name. + * + * @param string $name + */ + public function setName($name) + { + $this->name = $name; + } + + private function stampCreated($lifetime = null) + { + $timeStamp = time(); + $this->meta[self::CREATED] = $this->meta[self::UPDATED] = $this->lastUsed = $timeStamp; + $this->meta[self::LIFETIME] = (null === $lifetime) ? ini_get('session.cookie_lifetime') : $lifetime; + } +} diff --git a/lib/silex/vendor/symfony/http-foundation/Session/Storage/MockArraySessionStorage.php b/lib/silex/vendor/symfony/http-foundation/Session/Storage/MockArraySessionStorage.php new file mode 100644 index 000000000..027f4efff --- /dev/null +++ b/lib/silex/vendor/symfony/http-foundation/Session/Storage/MockArraySessionStorage.php @@ -0,0 +1,256 @@ + + * + * 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; + +use Symfony\Component\HttpFoundation\Session\SessionBagInterface; + +/** + * MockArraySessionStorage mocks the session for unit tests. + * + * No PHP session is actually started since a session can be initialized + * and shutdown only once per PHP execution cycle. + * + * When doing functional testing, you should use MockFileSessionStorage instead. + * + * @author Fabien Potencier + * @author Bulat Shakirzyanov + * @author Drak + */ +class MockArraySessionStorage implements SessionStorageInterface +{ + /** + * @var string + */ + protected $id = ''; + + /** + * @var string + */ + protected $name; + + /** + * @var bool + */ + protected $started = false; + + /** + * @var bool + */ + protected $closed = false; + + /** + * @var array + */ + protected $data = array(); + + /** + * @var MetadataBag + */ + protected $metadataBag; + + /** + * @var array|SessionBagInterface[] + */ + protected $bags = array(); + + /** + * @param string $name Session name + * @param MetadataBag $metaBag MetadataBag instance + */ + public function __construct($name = 'MOCKSESSID', MetadataBag $metaBag = null) + { + $this->name = $name; + $this->setMetadataBag($metaBag); + } + + public function setSessionData(array $array) + { + $this->data = $array; + } + + /** + * {@inheritdoc} + */ + public function start() + { + if ($this->started) { + return true; + } + + if (empty($this->id)) { + $this->id = $this->generateId(); + } + + $this->loadSession(); + + return true; + } + + /** + * {@inheritdoc} + */ + public function regenerate($destroy = false, $lifetime = null) + { + if (!$this->started) { + $this->start(); + } + + $this->metadataBag->stampNew($lifetime); + $this->id = $this->generateId(); + + return true; + } + + /** + * {@inheritdoc} + */ + public function getId() + { + return $this->id; + } + + /** + * {@inheritdoc} + */ + public function setId($id) + { + if ($this->started) { + throw new \LogicException('Cannot set session ID after the session has started.'); + } + + $this->id = $id; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return $this->name; + } + + /** + * {@inheritdoc} + */ + public function setName($name) + { + $this->name = $name; + } + + /** + * {@inheritdoc} + */ + public function save() + { + if (!$this->started || $this->closed) { + throw new \RuntimeException('Trying to save a session that was not started yet or was already closed'); + } + // nothing to do since we don't persist the session data + $this->closed = false; + $this->started = false; + } + + /** + * {@inheritdoc} + */ + public function clear() + { + // clear out the bags + foreach ($this->bags as $bag) { + $bag->clear(); + } + + // clear out the session + $this->data = array(); + + // reconnect the bags to the session + $this->loadSession(); + } + + /** + * {@inheritdoc} + */ + public function registerBag(SessionBagInterface $bag) + { + $this->bags[$bag->getName()] = $bag; + } + + /** + * {@inheritdoc} + */ + public function getBag($name) + { + if (!isset($this->bags[$name])) { + throw new \InvalidArgumentException(sprintf('The SessionBagInterface %s is not registered.', $name)); + } + + if (!$this->started) { + $this->start(); + } + + return $this->bags[$name]; + } + + /** + * {@inheritdoc} + */ + public function isStarted() + { + return $this->started; + } + + public function setMetadataBag(MetadataBag $bag = null) + { + if (null === $bag) { + $bag = new MetadataBag(); + } + + $this->metadataBag = $bag; + } + + /** + * Gets the MetadataBag. + * + * @return MetadataBag + */ + public function getMetadataBag() + { + return $this->metadataBag; + } + + /** + * Generates a session ID. + * + * This doesn't need to be particularly cryptographically secure since this is just + * a mock. + * + * @return string + */ + protected function generateId() + { + return hash('sha256', uniqid('ss_mock_', true)); + } + + protected function loadSession() + { + $bags = array_merge($this->bags, array($this->metadataBag)); + + foreach ($bags as $bag) { + $key = $bag->getStorageKey(); + $this->data[$key] = isset($this->data[$key]) ? $this->data[$key] : array(); + $bag->initialize($this->data[$key]); + } + + $this->started = true; + $this->closed = false; + } +} diff --git a/lib/silex/vendor/symfony/http-foundation/Session/Storage/MockFileSessionStorage.php b/lib/silex/vendor/symfony/http-foundation/Session/Storage/MockFileSessionStorage.php new file mode 100644 index 000000000..14f427007 --- /dev/null +++ b/lib/silex/vendor/symfony/http-foundation/Session/Storage/MockFileSessionStorage.php @@ -0,0 +1,152 @@ + + * + * 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; + +/** + * MockFileSessionStorage is used to mock sessions for + * functional testing when done in a single PHP process. + * + * No PHP session is actually started since a session can be initialized + * and shutdown only once per PHP execution cycle and this class does + * not pollute any session related globals, including session_*() functions + * or session.* PHP ini directives. + * + * @author Drak + */ +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 + */ + public function __construct($savePath = null, $name = 'MOCKSESSID', MetadataBag $metaBag = null) + { + if (null === $savePath) { + $savePath = sys_get_temp_dir(); + } + + if (!is_dir($savePath) && !@mkdir($savePath, 0777, true) && !is_dir($savePath)) { + throw new \RuntimeException(sprintf('Session Storage was not able to create directory "%s"', $savePath)); + } + + $this->savePath = $savePath; + + parent::__construct($name, $metaBag); + } + + /** + * {@inheritdoc} + */ + public function start() + { + if ($this->started) { + return true; + } + + if (!$this->id) { + $this->id = $this->generateId(); + } + + $this->read(); + + $this->started = true; + + return true; + } + + /** + * {@inheritdoc} + */ + public function regenerate($destroy = false, $lifetime = null) + { + if (!$this->started) { + $this->start(); + } + + if ($destroy) { + $this->destroy(); + } + + return parent::regenerate($destroy, $lifetime); + } + + /** + * {@inheritdoc} + */ + public function save() + { + if (!$this->started) { + throw new \RuntimeException('Trying to save a session that was not started yet or was already closed'); + } + + $data = $this->data; + + foreach ($this->bags as $bag) { + if (empty($data[$key = $bag->getStorageKey()])) { + unset($data[$key]); + } + } + if (array($key = $this->metadataBag->getStorageKey()) === array_keys($data)) { + unset($data[$key]); + } + + try { + if ($data) { + file_put_contents($this->getFilePath(), serialize($data)); + } else { + $this->destroy(); + } + } finally { + $this->data = $data; + } + + // this is needed for Silex, where the session object is re-used across requests + // in functional tests. In Symfony, the container is rebooted, so we don't have + // this issue + $this->started = false; + } + + /** + * Deletes a session from persistent storage. + * Deliberately leaves session data in memory intact. + */ + private function destroy() + { + if (is_file($this->getFilePath())) { + unlink($this->getFilePath()); + } + } + + /** + * Calculate path to file. + * + * @return string File path + */ + private function getFilePath() + { + return $this->savePath.'/'.$this->id.'.mocksess'; + } + + /** + * Reads session from storage and loads session. + */ + private function read() + { + $filePath = $this->getFilePath(); + $this->data = is_readable($filePath) && is_file($filePath) ? unserialize(file_get_contents($filePath)) : array(); + + $this->loadSession(); + } +} diff --git a/lib/silex/vendor/symfony/http-foundation/Session/Storage/NativeSessionStorage.php b/lib/silex/vendor/symfony/http-foundation/Session/Storage/NativeSessionStorage.php new file mode 100644 index 000000000..0dfad9acb --- /dev/null +++ b/lib/silex/vendor/symfony/http-foundation/Session/Storage/NativeSessionStorage.php @@ -0,0 +1,445 @@ + + * + * 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; + +use Symfony\Component\HttpFoundation\Session\SessionBagInterface; +use Symfony\Component\HttpFoundation\Session\Storage\Handler\StrictSessionHandler; +use Symfony\Component\HttpFoundation\Session\Storage\Proxy\AbstractProxy; +use Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy; + +/** + * This provides a base class for session attribute storage. + * + * @author Drak + */ +class NativeSessionStorage implements SessionStorageInterface +{ + /** + * @var SessionBagInterface[] + */ + protected $bags = array(); + + /** + * @var bool + */ + protected $started = false; + + /** + * @var bool + */ + protected $closed = false; + + /** + * @var AbstractProxy|\SessionHandlerInterface + */ + protected $saveHandler; + + /** + * @var MetadataBag + */ + protected $metadataBag; + + /** + * 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 + * 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 + * PHP starts to execute user-land code. Setting during runtime has no effect). + * + * cache_limiter, "" (use "0" to prevent headers from being sent entirely). + * cache_expire, "0" + * cookie_domain, "" + * cookie_httponly, "" + * cookie_lifetime, "0" + * cookie_path, "/" + * cookie_secure, "" + * entropy_file, "" + * entropy_length, "0" + * gc_divisor, "100" + * gc_maxlifetime, "1440" + * gc_probability, "1" + * hash_bits_per_character, "4" + * hash_function, "0" + * lazy_write, "1" + * name, "PHPSESSID" + * referer_check, "" + * serialize_handler, "php" + * use_strict_mode, "0" + * use_cookies, "1" + * use_only_cookies, "1" + * use_trans_sid, "0" + * upload_progress.enabled, "1" + * upload_progress.cleanup, "1" + * upload_progress.prefix, "upload_progress_" + * upload_progress.name, "PHP_SESSION_UPLOAD_PROGRESS" + * upload_progress.freq, "1%" + * upload_progress.min-freq, "1" + * url_rewriter.tags, "a=href,area=href,frame=src,form=,fieldset=" + * sid_length, "32" + * sid_bits_per_character, "5" + * 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 + */ + public function __construct(array $options = array(), $handler = null, MetadataBag $metaBag = null) + { + $options += array( + 'cache_limiter' => '', + 'cache_expire' => 0, + 'use_cookies' => 1, + 'lazy_write' => 1, + ); + + session_register_shutdown(); + + $this->setMetadataBag($metaBag); + $this->setOptions($options); + $this->setSaveHandler($handler); + } + + /** + * Gets the save handler instance. + * + * @return AbstractProxy|\SessionHandlerInterface + */ + public function getSaveHandler() + { + return $this->saveHandler; + } + + /** + * {@inheritdoc} + */ + public function start() + { + if ($this->started) { + return true; + } + + if (\PHP_SESSION_ACTIVE === session_status()) { + throw new \RuntimeException('Failed to start the session: already started by PHP.'); + } + + if (ini_get('session.use_cookies') && headers_sent($file, $line)) { + throw new \RuntimeException(sprintf('Failed to start the session because headers have already been sent by "%s" at line %d.', $file, $line)); + } + + // ok to try and start the session + if (!session_start()) { + throw new \RuntimeException('Failed to start the session'); + } + + $this->loadSession(); + + return true; + } + + /** + * {@inheritdoc} + */ + public function getId() + { + return $this->saveHandler->getId(); + } + + /** + * {@inheritdoc} + */ + public function setId($id) + { + $this->saveHandler->setId($id); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return $this->saveHandler->getName(); + } + + /** + * {@inheritdoc} + */ + public function setName($name) + { + $this->saveHandler->setName($name); + } + + /** + * {@inheritdoc} + */ + public function regenerate($destroy = false, $lifetime = null) + { + // Cannot regenerate the session ID for non-active sessions. + if (\PHP_SESSION_ACTIVE !== session_status()) { + return false; + } + + if (headers_sent()) { + return false; + } + + if (null !== $lifetime) { + ini_set('session.cookie_lifetime', $lifetime); + } + + if ($destroy) { + $this->metadataBag->stampNew(); + } + + $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 + $this->loadSession(); + + return $isRegenerated; + } + + /** + * {@inheritdoc} + */ + public function save() + { + $session = $_SESSION; + + foreach ($this->bags as $bag) { + if (empty($_SESSION[$key = $bag->getStorageKey()])) { + unset($_SESSION[$key]); + } + } + if (array($key = $this->metadataBag->getStorageKey()) === array_keys($_SESSION)) { + unset($_SESSION[$key]); + } + + // Register custom error handler to catch a possible failure warning during session write + set_error_handler(function ($errno, $errstr, $errfile, $errline) { + throw new \ErrorException($errstr, $errno, E_WARNING, $errfile, $errline); + }, E_WARNING); + + try { + $e = null; + session_write_close(); + } catch (\ErrorException $e) { + } finally { + restore_error_handler(); + $_SESSION = $session; + } + if (null !== $e) { + // The default PHP error message is not very helpful, as it does not give any information on the current save handler. + // Therefore, we catch this error and trigger a warning with a better error message + $handler = $this->getSaveHandler(); + if ($handler instanceof SessionHandlerProxy) { + $handler = $handler->getHandler(); + } + + trigger_error(sprintf('session_write_close(): Failed to write session data with %s handler', get_class($handler)), E_USER_WARNING); + } + + $this->closed = true; + $this->started = false; + } + + /** + * {@inheritdoc} + */ + public function clear() + { + // clear out the bags + foreach ($this->bags as $bag) { + $bag->clear(); + } + + // clear out the session + $_SESSION = array(); + + // reconnect the bags to the session + $this->loadSession(); + } + + /** + * {@inheritdoc} + */ + public function registerBag(SessionBagInterface $bag) + { + if ($this->started) { + throw new \LogicException('Cannot register a bag when the session is already started.'); + } + + $this->bags[$bag->getName()] = $bag; + } + + /** + * {@inheritdoc} + */ + public function getBag($name) + { + if (!isset($this->bags[$name])) { + throw new \InvalidArgumentException(sprintf('The SessionBagInterface %s is not registered.', $name)); + } + + if (!$this->started && $this->saveHandler->isActive()) { + $this->loadSession(); + } elseif (!$this->started) { + $this->start(); + } + + return $this->bags[$name]; + } + + public function setMetadataBag(MetadataBag $metaBag = null) + { + if (null === $metaBag) { + $metaBag = new MetadataBag(); + } + + $this->metadataBag = $metaBag; + } + + /** + * Gets the MetadataBag. + * + * @return MetadataBag + */ + public function getMetadataBag() + { + return $this->metadataBag; + } + + /** + * {@inheritdoc} + */ + public function isStarted() + { + return $this->started; + } + + /** + * Sets session.* ini variables. + * + * 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) + * + * @see http://php.net/session.configuration + */ + public function setOptions(array $options) + { + if (headers_sent() || \PHP_SESSION_ACTIVE === session_status()) { + return; + } + + $validOptions = array_flip(array( + 'cache_limiter', 'cache_expire', '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', + '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])) { + ini_set('session.'.$key, $value); + } + } + } + + /** + * Registers session save handler as a PHP session handler. + * + * To use internal PHP session save handlers, override this method using ini_set with + * session.save_handler and session.save_path e.g. + * + * ini_set('session.save_handler', 'files'); + * 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 + * + * @see http://php.net/session-set-save-handler + * @see http://php.net/sessionhandlerinterface + * @see http://php.net/sessionhandler + * @see http://github.com/drak/NativeSession + * + * @param \SessionHandlerInterface|null $saveHandler + * + * @throws \InvalidArgumentException + */ + public function setSaveHandler($saveHandler = null) + { + if (!$saveHandler instanceof AbstractProxy && + !$saveHandler instanceof \SessionHandlerInterface && + null !== $saveHandler) { + throw new \InvalidArgumentException('Must be instance of AbstractProxy; implement \SessionHandlerInterface; or be null.'); + } + + // Wrap $saveHandler in proxy and prevent double wrapping of proxy + if (!$saveHandler instanceof AbstractProxy && $saveHandler instanceof \SessionHandlerInterface) { + $saveHandler = new SessionHandlerProxy($saveHandler); + } elseif (!$saveHandler instanceof AbstractProxy) { + $saveHandler = new SessionHandlerProxy(new StrictSessionHandler(new \SessionHandler())); + } + $this->saveHandler = $saveHandler; + + if (headers_sent() || \PHP_SESSION_ACTIVE === session_status()) { + return; + } + + if ($this->saveHandler instanceof SessionHandlerProxy) { + session_set_save_handler($this->saveHandler->getHandler(), false); + } elseif ($this->saveHandler instanceof \SessionHandlerInterface) { + session_set_save_handler($this->saveHandler, false); + } + } + + /** + * Load the session with attributes. + * + * After starting the session, PHP retrieves the session from whatever handlers + * are set to (either PHP's internal, or a custom save handler set with session_set_save_handler()). + * PHP takes the return value from the read() handler, unserializes it + * and populates $_SESSION with the result automatically. + */ + protected function loadSession(array &$session = null) + { + if (null === $session) { + $session = &$_SESSION; + } + + $bags = array_merge($this->bags, array($this->metadataBag)); + + foreach ($bags as $bag) { + $key = $bag->getStorageKey(); + $session[$key] = isset($session[$key]) ? $session[$key] : array(); + $bag->initialize($session[$key]); + } + + $this->started = true; + $this->closed = false; + } +} diff --git a/lib/silex/vendor/symfony/http-foundation/Session/Storage/PhpBridgeSessionStorage.php b/lib/silex/vendor/symfony/http-foundation/Session/Storage/PhpBridgeSessionStorage.php new file mode 100644 index 000000000..662ed5015 --- /dev/null +++ b/lib/silex/vendor/symfony/http-foundation/Session/Storage/PhpBridgeSessionStorage.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\Storage; + +/** + * Allows session to be started by PHP and managed by Symfony. + * + * @author Drak + */ +class PhpBridgeSessionStorage extends NativeSessionStorage +{ + /** + * @param \SessionHandlerInterface|null $handler + * @param MetadataBag $metaBag MetadataBag + */ + public function __construct($handler = null, MetadataBag $metaBag = null) + { + $this->setMetadataBag($metaBag); + $this->setSaveHandler($handler); + } + + /** + * {@inheritdoc} + */ + public function start() + { + if ($this->started) { + return true; + } + + $this->loadSession(); + + return true; + } + + /** + * {@inheritdoc} + */ + public function clear() + { + // clear out the bags and nothing else that may be set + // since the purpose of this driver is to share a handler + foreach ($this->bags as $bag) { + $bag->clear(); + } + + // reconnect the bags to the session + $this->loadSession(); + } +} diff --git a/lib/silex/vendor/symfony/http-foundation/Session/Storage/Proxy/AbstractProxy.php b/lib/silex/vendor/symfony/http-foundation/Session/Storage/Proxy/AbstractProxy.php new file mode 100644 index 000000000..09c92483c --- /dev/null +++ b/lib/silex/vendor/symfony/http-foundation/Session/Storage/Proxy/AbstractProxy.php @@ -0,0 +1,122 @@ + + * + * 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; + +/** + * @author Drak + */ +abstract class AbstractProxy +{ + /** + * Flag if handler wraps an internal PHP session handler (using \SessionHandler). + * + * @var bool + */ + protected $wrapper = false; + + /** + * @var string + */ + protected $saveHandlerName; + + /** + * Gets the session.save_handler name. + * + * @return string + */ + public function getSaveHandlerName() + { + return $this->saveHandlerName; + } + + /** + * Is this proxy handler and instance of \SessionHandlerInterface. + * + * @return bool + */ + public function isSessionHandlerInterface() + { + return $this instanceof \SessionHandlerInterface; + } + + /** + * Returns true if this handler wraps an internal PHP session save handler using \SessionHandler. + * + * @return bool + */ + public function isWrapper() + { + return $this->wrapper; + } + + /** + * Has a session started? + * + * @return bool + */ + public function isActive() + { + return \PHP_SESSION_ACTIVE === session_status(); + } + + /** + * Gets the session ID. + * + * @return string + */ + public function getId() + { + return session_id(); + } + + /** + * Sets the session ID. + * + * @param string $id + * + * @throws \LogicException + */ + public function setId($id) + { + if ($this->isActive()) { + throw new \LogicException('Cannot change the ID of an active session'); + } + + session_id($id); + } + + /** + * Gets the session name. + * + * @return string + */ + public function getName() + { + return session_name(); + } + + /** + * Sets the session name. + * + * @param string $name + * + * @throws \LogicException + */ + public function setName($name) + { + if ($this->isActive()) { + throw new \LogicException('Cannot change the name of an active session'); + } + + session_name($name); + } +} diff --git a/lib/silex/vendor/symfony/http-foundation/Session/Storage/Proxy/NativeProxy.php b/lib/silex/vendor/symfony/http-foundation/Session/Storage/Proxy/NativeProxy.php new file mode 100644 index 000000000..082eed143 --- /dev/null +++ b/lib/silex/vendor/symfony/http-foundation/Session/Storage/Proxy/NativeProxy.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\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/lib/silex/vendor/symfony/http-foundation/Session/Storage/Proxy/SessionHandlerProxy.php b/lib/silex/vendor/symfony/http-foundation/Session/Storage/Proxy/SessionHandlerProxy.php new file mode 100644 index 000000000..53c1209a1 --- /dev/null +++ b/lib/silex/vendor/symfony/http-foundation/Session/Storage/Proxy/SessionHandlerProxy.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\Proxy; + +/** + * @author Drak + */ +class SessionHandlerProxy extends AbstractProxy implements \SessionHandlerInterface +{ + protected $handler; + + public function __construct(\SessionHandlerInterface $handler) + { + $this->handler = $handler; + $this->wrapper = ($handler instanceof \SessionHandler); + $this->saveHandlerName = $this->wrapper ? ini_get('session.save_handler') : 'user'; + } + + /** + * @return \SessionHandlerInterface + */ + public function getHandler() + { + return $this->handler; + } + + // \SessionHandlerInterface + + /** + * {@inheritdoc} + */ + public function open($savePath, $sessionName) + { + return (bool) $this->handler->open($savePath, $sessionName); + } + + /** + * {@inheritdoc} + */ + public function close() + { + return (bool) $this->handler->close(); + } + + /** + * {@inheritdoc} + */ + public function read($sessionId) + { + return (string) $this->handler->read($sessionId); + } + + /** + * {@inheritdoc} + */ + public function write($sessionId, $data) + { + return (bool) $this->handler->write($sessionId, $data); + } + + /** + * {@inheritdoc} + */ + public function destroy($sessionId) + { + return (bool) $this->handler->destroy($sessionId); + } + + /** + * {@inheritdoc} + */ + public function gc($maxlifetime) + { + return (bool) $this->handler->gc($maxlifetime); + } +} diff --git a/lib/silex/vendor/symfony/http-foundation/Session/Storage/SessionStorageInterface.php b/lib/silex/vendor/symfony/http-foundation/Session/Storage/SessionStorageInterface.php new file mode 100644 index 000000000..66e8b33dd --- /dev/null +++ b/lib/silex/vendor/symfony/http-foundation/Session/Storage/SessionStorageInterface.php @@ -0,0 +1,137 @@ + + * + * 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; + +use Symfony\Component\HttpFoundation\Session\SessionBagInterface; + +/** + * StorageInterface. + * + * @author Fabien Potencier + * @author Drak + */ +interface SessionStorageInterface +{ + /** + * Starts the session. + * + * @return bool True if started + * + * @throws \RuntimeException if something goes wrong starting the session + */ + public function start(); + + /** + * Checks if the session is started. + * + * @return bool True if started, false otherwise + */ + public function isStarted(); + + /** + * Returns the session ID. + * + * @return string The session ID or empty + */ + public function getId(); + + /** + * Sets the session ID. + * + * @param string $id + */ + public function setId($id); + + /** + * Returns the session name. + * + * @return mixed The session name + */ + public function getName(); + + /** + * Sets the session name. + * + * @param string $name + */ + public function setName($name); + + /** + * Regenerates id that represents this storage. + * + * This method must invoke session_regenerate_id($destroy) unless + * this interface is used for a storage object designed for unit + * or functional testing where a real PHP session would interfere + * with testing. + * + * Note regenerate+destroy should not clear the session data in memory + * 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. + * 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. + * Otherwise session data could get lost again for concurrent requests with the + * new ID. One result could be that you get logged out after just logging in. + * + * @param bool $destroy Destroy session when regenerating? + * @param int $lifetime Sets the cookie lifetime for the session cookie. A null value + * will leave the system settings unchanged, 0 sets the cookie + * to expire with browser session. Time is in seconds, and is + * not a Unix timestamp. + * + * @return bool True if session regenerated, false if error + * + * @throws \RuntimeException If an error occurs while regenerating this storage + */ + public function regenerate($destroy = false, $lifetime = null); + + /** + * Force the session to be saved and closed. + * + * This method must invoke session_write_close() unless this interface is + * used for a storage object design for unit or functional testing where + * a real PHP session would interfere with testing, in which case + * it should actually persist the session data if required. + * + * @throws \RuntimeException if the session is saved without being started, or if the session + * is already closed + */ + public function save(); + + /** + * Clear all session data in memory. + */ + public function clear(); + + /** + * Gets a SessionBagInterface by name. + * + * @param string $name + * + * @return SessionBagInterface + * + * @throws \InvalidArgumentException If the bag does not exist + */ + public function getBag($name); + + /** + * Registers a SessionBagInterface for use. + */ + public function registerBag(SessionBagInterface $bag); + + /** + * @return MetadataBag + */ + public function getMetadataBag(); +} diff --git a/lib/silex/vendor/symfony/http-foundation/StreamedResponse.php b/lib/silex/vendor/symfony/http-foundation/StreamedResponse.php new file mode 100644 index 000000000..92868d33e --- /dev/null +++ b/lib/silex/vendor/symfony/http-foundation/StreamedResponse.php @@ -0,0 +1,144 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation; + +/** + * StreamedResponse represents a streamed HTTP response. + * + * A StreamedResponse uses a callback for its content. + * + * The callback should use the standard PHP functions like echo + * to stream the response back to the client. The flush() method + * can also be used if needed. + * + * @see flush() + * + * @author Fabien Potencier + */ +class StreamedResponse extends Response +{ + protected $callback; + protected $streamed; + private $headersSent; + + /** + * @param callable|null $callback A valid PHP callback or null to set it later + * @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()) + { + parent::__construct(null, $status, $headers); + + if (null !== $callback) { + $this->setCallback($callback); + } + $this->streamed = false; + $this->headersSent = false; + } + + /** + * Factory method for chainability. + * + * @param callable|null $callback A valid PHP callback or null to set it later + * @param int $status The response status code + * @param array $headers An array of response headers + * + * @return static + */ + public static function create($callback = null, $status = 200, $headers = array()) + { + return new static($callback, $status, $headers); + } + + /** + * Sets the PHP callback associated with this Response. + * + * @param callable $callback A valid PHP callback + * + * @return $this + */ + public function setCallback(callable $callback) + { + $this->callback = $callback; + + return $this; + } + + /** + * {@inheritdoc} + * + * This method only sends the headers once. + * + * @return $this + */ + public function sendHeaders() + { + if ($this->headersSent) { + return $this; + } + + $this->headersSent = true; + + return parent::sendHeaders(); + } + + /** + * {@inheritdoc} + * + * This method only sends the content once. + * + * @return $this + */ + public function sendContent() + { + if ($this->streamed) { + return $this; + } + + $this->streamed = true; + + if (null === $this->callback) { + throw new \LogicException('The Response callback must not be null.'); + } + + call_user_func($this->callback); + + return $this; + } + + /** + * {@inheritdoc} + * + * @throws \LogicException when the content is not null + * + * @return $this + */ + public function setContent($content) + { + if (null !== $content) { + throw new \LogicException('The content cannot be set on a StreamedResponse instance.'); + } + + return $this; + } + + /** + * {@inheritdoc} + * + * @return false + */ + public function getContent() + { + return false; + } +} diff --git a/lib/silex/vendor/symfony/http-foundation/Tests/AcceptHeaderItemTest.php b/lib/silex/vendor/symfony/http-foundation/Tests/AcceptHeaderItemTest.php new file mode 100644 index 000000000..cb43bb351 --- /dev/null +++ b/lib/silex/vendor/symfony/http-foundation/Tests/AcceptHeaderItemTest.php @@ -0,0 +1,113 @@ + + * + * 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/lib/silex/vendor/symfony/http-foundation/Tests/AcceptHeaderTest.php b/lib/silex/vendor/symfony/http-foundation/Tests/AcceptHeaderTest.php new file mode 100644 index 000000000..9929eac28 --- /dev/null +++ b/lib/silex/vendor/symfony/http-foundation/Tests/AcceptHeaderTest.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\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/lib/silex/vendor/symfony/http-foundation/Tests/ApacheRequestTest.php b/lib/silex/vendor/symfony/http-foundation/Tests/ApacheRequestTest.php new file mode 100644 index 000000000..157ab90ec --- /dev/null +++ b/lib/silex/vendor/symfony/http-foundation/Tests/ApacheRequestTest.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\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/lib/silex/vendor/symfony/http-foundation/Tests/BinaryFileResponseTest.php b/lib/silex/vendor/symfony/http-foundation/Tests/BinaryFileResponseTest.php new file mode 100644 index 000000000..1b9e58991 --- /dev/null +++ b/lib/silex/vendor/symfony/http-foundation/Tests/BinaryFileResponseTest.php @@ -0,0 +1,352 @@ + + * + * 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'), + ); + } + + /** + * @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/lib/silex/vendor/symfony/http-foundation/Tests/CookieTest.php b/lib/silex/vendor/symfony/http-foundation/Tests/CookieTest.php new file mode 100644 index 000000000..070b7dd42 --- /dev/null +++ b/lib/silex/vendor/symfony/http-foundation/Tests/CookieTest.php @@ -0,0 +1,223 @@ + + * + * 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'); + } + + 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='.($expire - time()).'; 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='.($expire - time()).'; 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='.($expire - time()).'; 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($expire - time(), $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/lib/silex/vendor/symfony/http-foundation/Tests/ExpressionRequestMatcherTest.php b/lib/silex/vendor/symfony/http-foundation/Tests/ExpressionRequestMatcherTest.php new file mode 100644 index 000000000..1152e46c0 --- /dev/null +++ b/lib/silex/vendor/symfony/http-foundation/Tests/ExpressionRequestMatcherTest.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\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/lib/silex/vendor/symfony/http-foundation/Tests/File/FakeFile.php b/lib/silex/vendor/symfony/http-foundation/Tests/File/FakeFile.php new file mode 100644 index 000000000..c415989f2 --- /dev/null +++ b/lib/silex/vendor/symfony/http-foundation/Tests/File/FakeFile.php @@ -0,0 +1,45 @@ + + * + * 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/lib/silex/vendor/symfony/http-foundation/Tests/File/FileTest.php b/lib/silex/vendor/symfony/http-foundation/Tests/File/FileTest.php new file mode 100644 index 000000000..dbd9c44bd --- /dev/null +++ b/lib/silex/vendor/symfony/http-foundation/Tests/File/FileTest.php @@ -0,0 +1,180 @@ + + * + * 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/lib/silex/vendor/symfony/http-foundation/Tests/File/Fixtures/.unknownextension b/lib/silex/vendor/symfony/http-foundation/Tests/File/Fixtures/.unknownextension new file mode 100644 index 000000000..4d1ae35ba --- /dev/null +++ b/lib/silex/vendor/symfony/http-foundation/Tests/File/Fixtures/.unknownextension @@ -0,0 +1 @@ +f \ No newline at end of file diff --git a/lib/silex/vendor/symfony/http-foundation/Tests/File/Fixtures/directory/.empty b/lib/silex/vendor/symfony/http-foundation/Tests/File/Fixtures/directory/.empty new file mode 100644 index 000000000..e69de29bb diff --git a/lib/silex/vendor/symfony/http-foundation/Tests/File/Fixtures/other-file.example b/lib/silex/vendor/symfony/http-foundation/Tests/File/Fixtures/other-file.example new file mode 100644 index 000000000..e69de29bb diff --git a/lib/silex/vendor/symfony/http-foundation/Tests/File/Fixtures/test b/lib/silex/vendor/symfony/http-foundation/Tests/File/Fixtures/test new file mode 100644 index 0000000000000000000000000000000000000000..b636f4b8df536b0a85e7cea1a6cf3f0bd3179b96 GIT binary patch literal 35 jcmZ?wbh9u|WMp7uXkcLY4+c66KmZb9U}AD%WUvMRyAlZ1 literal 0 HcmV?d00001 diff --git a/lib/silex/vendor/symfony/http-foundation/Tests/File/Fixtures/test.gif b/lib/silex/vendor/symfony/http-foundation/Tests/File/Fixtures/test.gif new file mode 100644 index 0000000000000000000000000000000000000000..b636f4b8df536b0a85e7cea1a6cf3f0bd3179b96 GIT binary patch literal 35 jcmZ?wbh9u|WMp7uXkcLY4+c66KmZb9U}AD%WUvMRyAlZ1 literal 0 HcmV?d00001 diff --git a/lib/silex/vendor/symfony/http-foundation/Tests/File/MimeType/MimeTypeTest.php b/lib/silex/vendor/symfony/http-foundation/Tests/File/MimeType/MimeTypeTest.php new file mode 100644 index 000000000..b3f1f026a --- /dev/null +++ b/lib/silex/vendor/symfony/http-foundation/Tests/File/MimeType/MimeTypeTest.php @@ -0,0 +1,90 @@ + + * + * 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\MimeTypeGuesser; +use Symfony\Component\HttpFoundation\File\MimeType\FileBinaryMimeTypeGuesser; + +/** + * @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/lib/silex/vendor/symfony/http-foundation/Tests/File/UploadedFileTest.php b/lib/silex/vendor/symfony/http-foundation/Tests/File/UploadedFileTest.php new file mode 100644 index 000000000..36f122fe7 --- /dev/null +++ b/lib/silex/vendor/symfony/http-foundation/Tests/File/UploadedFileTest.php @@ -0,0 +1,273 @@ + + * + * 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/lib/silex/vendor/symfony/http-foundation/Tests/FileBagTest.php b/lib/silex/vendor/symfony/http-foundation/Tests/FileBagTest.php new file mode 100644 index 000000000..b1bbba0d3 --- /dev/null +++ b/lib/silex/vendor/symfony/http-foundation/Tests/FileBagTest.php @@ -0,0 +1,175 @@ + + * + * 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/lib/silex/vendor/symfony/http-foundation/Tests/HeaderBagTest.php b/lib/silex/vendor/symfony/http-foundation/Tests/HeaderBagTest.php new file mode 100644 index 000000000..6d19ceb00 --- /dev/null +++ b/lib/silex/vendor/symfony/http-foundation/Tests/HeaderBagTest.php @@ -0,0 +1,205 @@ + + * + * 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/lib/silex/vendor/symfony/http-foundation/Tests/IpUtilsTest.php b/lib/silex/vendor/symfony/http-foundation/Tests/IpUtilsTest.php new file mode 100644 index 000000000..7a93f9947 --- /dev/null +++ b/lib/silex/vendor/symfony/http-foundation/Tests/IpUtilsTest.php @@ -0,0 +1,104 @@ + + * + * 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/lib/silex/vendor/symfony/http-foundation/Tests/JsonResponseTest.php b/lib/silex/vendor/symfony/http-foundation/Tests/JsonResponseTest.php new file mode 100644 index 000000000..201839f89 --- /dev/null +++ b/lib/silex/vendor/symfony/http-foundation/Tests/JsonResponseTest.php @@ -0,0 +1,257 @@ + + * + * 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 +{ + 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/lib/silex/vendor/symfony/http-foundation/Tests/ParameterBagTest.php b/lib/silex/vendor/symfony/http-foundation/Tests/ParameterBagTest.php new file mode 100644 index 000000000..ab908d8d3 --- /dev/null +++ b/lib/silex/vendor/symfony/http-foundation/Tests/ParameterBagTest.php @@ -0,0 +1,194 @@ + + * + * 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/lib/silex/vendor/symfony/http-foundation/Tests/RedirectResponseTest.php b/lib/silex/vendor/symfony/http-foundation/Tests/RedirectResponseTest.php new file mode 100644 index 000000000..d389e83db --- /dev/null +++ b/lib/silex/vendor/symfony/http-foundation/Tests/RedirectResponseTest.php @@ -0,0 +1,97 @@ + + * + * 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/lib/silex/vendor/symfony/http-foundation/Tests/RequestMatcherTest.php b/lib/silex/vendor/symfony/http-foundation/Tests/RequestMatcherTest.php new file mode 100644 index 000000000..b5d80048f --- /dev/null +++ b/lib/silex/vendor/symfony/http-foundation/Tests/RequestMatcherTest.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\HttpFoundation\Tests; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\HttpFoundation\RequestMatcher; +use Symfony\Component\HttpFoundation\Request; + +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/lib/silex/vendor/symfony/http-foundation/Tests/RequestStackTest.php b/lib/silex/vendor/symfony/http-foundation/Tests/RequestStackTest.php new file mode 100644 index 000000000..a84fb26f0 --- /dev/null +++ b/lib/silex/vendor/symfony/http-foundation/Tests/RequestStackTest.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\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/lib/silex/vendor/symfony/http-foundation/Tests/RequestTest.php b/lib/silex/vendor/symfony/http-foundation/Tests/RequestTest.php new file mode 100644 index 000000000..72add9535 --- /dev/null +++ b/lib/silex/vendor/symfony/http-foundation/Tests/RequestTest.php @@ -0,0 +1,2329 @@ + + * + * 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\Session\Storage\MockArraySessionStorage; +use Symfony\Component\HttpFoundation\Session\Session; +use Symfony\Component\HttpFoundation\Request; + +class RequestTest extends TestCase +{ + protected function tearDown() + { + // reset + Request::setTrustedProxies(array(), -1); + } + + 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()); + } + + 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')); + } + + /** + * @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'); + } + + /** + * @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 + 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'), Request::HEADER_X_FORWARDED_ALL); + + $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( + 'X_ORIGINAL_URL' => '/foo/bar', + ), + array(), + '/foo/bar', + ), + array( + array( + 'X_REWRITE_URL' => '/foo/bar', + ), + array(), + '/foo/bar', + ), + array( + array(), + array( + 'IIS_WasUrlRewritten' => '1', + 'UNENCODED_URL' => '/foo/bar', + ), + '/foo/bar', + ), + array( + array( + 'X_ORIGINAL_URL' => '/foo/bar', + ), + array( + 'HTTP_X_ORIGINAL_URL' => '/foo/bar', + ), + '/foo/bar', + ), + array( + array( + 'X_ORIGINAL_URL' => '/foo/bar', + ), + array( + 'IIS_WasUrlRewritten' => '1', + 'UNENCODED_URL' => '/foo/bar', + ), + '/foo/bar', + ), + array( + array( + 'X_ORIGINAL_URL' => '/foo/bar', + ), + array( + 'HTTP_X_ORIGINAL_URL' => '/foo/bar', + '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()); + + // reset request for following tests + Request::setTrustedHosts(array()); + } + + 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, $chacheable) + { + $request = new Request(); + $request->setMethod($method); + $this->assertEquals($chacheable, $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()); + } +} + +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/lib/silex/vendor/symfony/http-foundation/Tests/ResponseHeaderBagTest.php b/lib/silex/vendor/symfony/http-foundation/Tests/ResponseHeaderBagTest.php new file mode 100644 index 000000000..ce8553590 --- /dev/null +++ b/lib/silex/vendor/symfony/http-foundation/Tests/ResponseHeaderBagTest.php @@ -0,0 +1,363 @@ + + * + * 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\ResponseHeaderBag; +use Symfony\Component\HttpFoundation\Cookie; + +/** + * @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=-31536001; 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=-31536001; 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/lib/silex/vendor/symfony/http-foundation/Tests/ResponseTest.php b/lib/silex/vendor/symfony/http-foundation/Tests/ResponseTest.php new file mode 100644 index 000000000..350d972a9 --- /dev/null +++ b/lib/silex/vendor/symfony/http-foundation/Tests/ResponseTest.php @@ -0,0 +1,1003 @@ + + * + * 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(); + $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()); + } + + 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'); + } + + 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/lib/silex/vendor/symfony/http-foundation/Tests/ResponseTestCase.php b/lib/silex/vendor/symfony/http-foundation/Tests/ResponseTestCase.php new file mode 100644 index 000000000..4ead34c10 --- /dev/null +++ b/lib/silex/vendor/symfony/http-foundation/Tests/ResponseTestCase.php @@ -0,0 +1,89 @@ + + * + * 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/lib/silex/vendor/symfony/http-foundation/Tests/ServerBagTest.php b/lib/silex/vendor/symfony/http-foundation/Tests/ServerBagTest.php new file mode 100644 index 000000000..f8becec5a --- /dev/null +++ b/lib/silex/vendor/symfony/http-foundation/Tests/ServerBagTest.php @@ -0,0 +1,170 @@ + + * + * 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/lib/silex/vendor/symfony/http-foundation/Tests/Session/Attribute/AttributeBagTest.php b/lib/silex/vendor/symfony/http-foundation/Tests/Session/Attribute/AttributeBagTest.php new file mode 100644 index 000000000..724a0b984 --- /dev/null +++ b/lib/silex/vendor/symfony/http-foundation/Tests/Session/Attribute/AttributeBagTest.php @@ -0,0 +1,186 @@ + + * + * 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/lib/silex/vendor/symfony/http-foundation/Tests/Session/Attribute/NamespacedAttributeBagTest.php b/lib/silex/vendor/symfony/http-foundation/Tests/Session/Attribute/NamespacedAttributeBagTest.php new file mode 100644 index 000000000..f074ce1b2 --- /dev/null +++ b/lib/silex/vendor/symfony/http-foundation/Tests/Session/Attribute/NamespacedAttributeBagTest.php @@ -0,0 +1,182 @@ + + * + * 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 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 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/lib/silex/vendor/symfony/http-foundation/Tests/Session/Flash/AutoExpireFlashBagTest.php b/lib/silex/vendor/symfony/http-foundation/Tests/Session/Flash/AutoExpireFlashBagTest.php new file mode 100644 index 000000000..fa8626ab9 --- /dev/null +++ b/lib/silex/vendor/symfony/http-foundation/Tests/Session/Flash/AutoExpireFlashBagTest.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\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/lib/silex/vendor/symfony/http-foundation/Tests/Session/Flash/FlashBagTest.php b/lib/silex/vendor/symfony/http-foundation/Tests/Session/Flash/FlashBagTest.php new file mode 100644 index 000000000..c4e75b1b1 --- /dev/null +++ b/lib/silex/vendor/symfony/http-foundation/Tests/Session/Flash/FlashBagTest.php @@ -0,0 +1,132 @@ + + * + * 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 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 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/lib/silex/vendor/symfony/http-foundation/Tests/Session/SessionTest.php b/lib/silex/vendor/symfony/http-foundation/Tests/Session/SessionTest.php new file mode 100644 index 000000000..41720e4b6 --- /dev/null +++ b/lib/silex/vendor/symfony/http-foundation/Tests/Session/SessionTest.php @@ -0,0 +1,242 @@ + + * + * 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\Session; +use Symfony\Component\HttpFoundation\Session\Flash\FlashBag; +use Symfony\Component\HttpFoundation\Session\Attribute\AttributeBag; +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 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/lib/silex/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/AbstractSessionHandlerTest.php b/lib/silex/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/AbstractSessionHandlerTest.php new file mode 100644 index 000000000..3ac081e38 --- /dev/null +++ b/lib/silex/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/AbstractSessionHandlerTest.php @@ -0,0 +1,61 @@ + + * + * 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/lib/silex/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/common.inc b/lib/silex/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/common.inc new file mode 100644 index 000000000..7a064c7f3 --- /dev/null +++ b/lib/silex/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/common.inc @@ -0,0 +1,151 @@ +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/lib/silex/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/empty_destroys.expected b/lib/silex/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/empty_destroys.expected new file mode 100644 index 000000000..820371474 --- /dev/null +++ b/lib/silex/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/empty_destroys.expected @@ -0,0 +1,17 @@ +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/lib/silex/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/empty_destroys.php b/lib/silex/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/empty_destroys.php new file mode 100644 index 000000000..3cfc1250a --- /dev/null +++ b/lib/silex/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/empty_destroys.php @@ -0,0 +1,8 @@ + Content-Type: text/plain; charset=utf-8 + [1] => Cache-Control: max-age=10800, private, must-revalidate +) +shutdown diff --git a/lib/silex/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/read_only.php b/lib/silex/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/read_only.php new file mode 100644 index 000000000..3e62fb9ec --- /dev/null +++ b/lib/silex/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/read_only.php @@ -0,0 +1,8 @@ + 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/lib/silex/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/regenerate.php b/lib/silex/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/regenerate.php new file mode 100644 index 000000000..a0f635c87 --- /dev/null +++ b/lib/silex/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/regenerate.php @@ -0,0 +1,10 @@ + 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/lib/silex/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/storage.php b/lib/silex/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/storage.php new file mode 100644 index 000000000..96dca3c2c --- /dev/null +++ b/lib/silex/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/storage.php @@ -0,0 +1,24 @@ +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/lib/silex/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/with_cookie.expected b/lib/silex/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/with_cookie.expected new file mode 100644 index 000000000..33da0a5be --- /dev/null +++ b/lib/silex/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/with_cookie.expected @@ -0,0 +1,15 @@ +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/lib/silex/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/with_cookie.php b/lib/silex/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/with_cookie.php new file mode 100644 index 000000000..ffb5b20a3 --- /dev/null +++ b/lib/silex/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/with_cookie.php @@ -0,0 +1,8 @@ + 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/lib/silex/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/with_cookie_and_session.php b/lib/silex/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/with_cookie_and_session.php new file mode 100644 index 000000000..ec5119323 --- /dev/null +++ b/lib/silex/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/with_cookie_and_session.php @@ -0,0 +1,13 @@ + + * + * 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/lib/silex/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/MemcachedSessionHandlerTest.php b/lib/silex/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/MemcachedSessionHandlerTest.php new file mode 100644 index 000000000..2e7be359e --- /dev/null +++ b/lib/silex/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/MemcachedSessionHandlerTest.php @@ -0,0 +1,139 @@ + + * + * 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/lib/silex/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/MongoDbSessionHandlerTest.php b/lib/silex/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/MongoDbSessionHandlerTest.php new file mode 100644 index 000000000..da051096c --- /dev/null +++ b/lib/silex/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/MongoDbSessionHandlerTest.php @@ -0,0 +1,333 @@ + + * + * 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/lib/silex/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/NativeFileSessionHandlerTest.php b/lib/silex/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/NativeFileSessionHandlerTest.php new file mode 100644 index 000000000..a6264e51d --- /dev/null +++ b/lib/silex/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/NativeFileSessionHandlerTest.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\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/lib/silex/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/NativeSessionHandlerTest.php b/lib/silex/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/NativeSessionHandlerTest.php new file mode 100644 index 000000000..1a68d575e --- /dev/null +++ b/lib/silex/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/NativeSessionHandlerTest.php @@ -0,0 +1,38 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\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->assertTrue($handler instanceof \SessionHandler); + $this->assertTrue($handler instanceof NativeSessionHandler); + } +} diff --git a/lib/silex/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/NullSessionHandlerTest.php b/lib/silex/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/NullSessionHandlerTest.php new file mode 100644 index 000000000..718fd0f83 --- /dev/null +++ b/lib/silex/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/NullSessionHandlerTest.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\Tests\Session\Storage\Handler; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\HttpFoundation\Session\Storage\Handler\NullSessionHandler; +use Symfony\Component\HttpFoundation\Session\Storage\NativeSessionStorage; +use Symfony\Component\HttpFoundation\Session\Session; + +/** + * 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/lib/silex/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/PdoSessionHandlerTest.php b/lib/silex/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/PdoSessionHandlerTest.php new file mode 100644 index 000000000..a0d7529f0 --- /dev/null +++ b/lib/silex/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/PdoSessionHandlerTest.php @@ -0,0 +1,376 @@ + + * + * 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 (ini_get('session.use_strict_mode')) { + $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)); + } + + 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/lib/silex/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/StrictSessionHandlerTest.php b/lib/silex/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/StrictSessionHandlerTest.php new file mode 100644 index 000000000..b02c41ae8 --- /dev/null +++ b/lib/silex/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/StrictSessionHandlerTest.php @@ -0,0 +1,189 @@ + + * + * 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/lib/silex/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/WriteCheckSessionHandlerTest.php b/lib/silex/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/WriteCheckSessionHandlerTest.php new file mode 100644 index 000000000..898a7d11a --- /dev/null +++ b/lib/silex/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/WriteCheckSessionHandlerTest.php @@ -0,0 +1,97 @@ + + * + * 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/lib/silex/vendor/symfony/http-foundation/Tests/Session/Storage/MetadataBagTest.php b/lib/silex/vendor/symfony/http-foundation/Tests/Session/Storage/MetadataBagTest.php new file mode 100644 index 000000000..69cf6163c --- /dev/null +++ b/lib/silex/vendor/symfony/http-foundation/Tests/Session/Storage/MetadataBagTest.php @@ -0,0 +1,139 @@ + + * + * 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/lib/silex/vendor/symfony/http-foundation/Tests/Session/Storage/MockArraySessionStorageTest.php b/lib/silex/vendor/symfony/http-foundation/Tests/Session/Storage/MockArraySessionStorageTest.php new file mode 100644 index 000000000..82df5543e --- /dev/null +++ b/lib/silex/vendor/symfony/http-foundation/Tests/Session/Storage/MockArraySessionStorageTest.php @@ -0,0 +1,131 @@ + + * + * 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\MockArraySessionStorage; +use Symfony\Component\HttpFoundation\Session\Attribute\AttributeBag; +use Symfony\Component\HttpFoundation\Session\Flash\FlashBag; + +/** + * 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/lib/silex/vendor/symfony/http-foundation/Tests/Session/Storage/MockFileSessionStorageTest.php b/lib/silex/vendor/symfony/http-foundation/Tests/Session/Storage/MockFileSessionStorageTest.php new file mode 100644 index 000000000..53accd384 --- /dev/null +++ b/lib/silex/vendor/symfony/http-foundation/Tests/Session/Storage/MockFileSessionStorageTest.php @@ -0,0 +1,127 @@ + + * + * 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\MockFileSessionStorage; +use Symfony\Component\HttpFoundation\Session\Flash\FlashBag; +use Symfony\Component\HttpFoundation\Session\Attribute\AttributeBag; + +/** + * 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/lib/silex/vendor/symfony/http-foundation/Tests/Session/Storage/NativeSessionStorageTest.php b/lib/silex/vendor/symfony/http-foundation/Tests/Session/Storage/NativeSessionStorageTest.php new file mode 100644 index 000000000..8fb8b4222 --- /dev/null +++ b/lib/silex/vendor/symfony/http-foundation/Tests/Session/Storage/NativeSessionStorageTest.php @@ -0,0 +1,277 @@ + + * + * 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); + } + + /** + * @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/lib/silex/vendor/symfony/http-foundation/Tests/Session/Storage/PhpBridgeSessionStorageTest.php b/lib/silex/vendor/symfony/http-foundation/Tests/Session/Storage/PhpBridgeSessionStorageTest.php new file mode 100644 index 000000000..958dc0bc0 --- /dev/null +++ b/lib/silex/vendor/symfony/http-foundation/Tests/Session/Storage/PhpBridgeSessionStorageTest.php @@ -0,0 +1,96 @@ + + * + * 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\PhpBridgeSessionStorage; +use Symfony\Component\HttpFoundation\Session\Attribute\AttributeBag; + +/** + * 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/lib/silex/vendor/symfony/http-foundation/Tests/Session/Storage/Proxy/AbstractProxyTest.php b/lib/silex/vendor/symfony/http-foundation/Tests/Session/Storage/Proxy/AbstractProxyTest.php new file mode 100644 index 000000000..cbb291f19 --- /dev/null +++ b/lib/silex/vendor/symfony/http-foundation/Tests/Session/Storage/Proxy/AbstractProxyTest.php @@ -0,0 +1,113 @@ + + * + * 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/lib/silex/vendor/symfony/http-foundation/Tests/Session/Storage/Proxy/NativeProxyTest.php b/lib/silex/vendor/symfony/http-foundation/Tests/Session/Storage/Proxy/NativeProxyTest.php new file mode 100644 index 000000000..ed4fee6bf --- /dev/null +++ b/lib/silex/vendor/symfony/http-foundation/Tests/Session/Storage/Proxy/NativeProxyTest.php @@ -0,0 +1,38 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\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/lib/silex/vendor/symfony/http-foundation/Tests/Session/Storage/Proxy/SessionHandlerProxyTest.php b/lib/silex/vendor/symfony/http-foundation/Tests/Session/Storage/Proxy/SessionHandlerProxyTest.php new file mode 100644 index 000000000..682825356 --- /dev/null +++ b/lib/silex/vendor/symfony/http-foundation/Tests/Session/Storage/Proxy/SessionHandlerProxyTest.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\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); + } +} diff --git a/lib/silex/vendor/symfony/http-foundation/Tests/StreamedResponseTest.php b/lib/silex/vendor/symfony/http-foundation/Tests/StreamedResponseTest.php new file mode 100644 index 000000000..c2ded996f --- /dev/null +++ b/lib/silex/vendor/symfony/http-foundation/Tests/StreamedResponseTest.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\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()); + } +} diff --git a/lib/silex/vendor/symfony/http-foundation/Tests/schema/http-status-codes.rng b/lib/silex/vendor/symfony/http-foundation/Tests/schema/http-status-codes.rng new file mode 100644 index 000000000..73708ca68 --- /dev/null +++ b/lib/silex/vendor/symfony/http-foundation/Tests/schema/http-status-codes.rng @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/silex/vendor/symfony/http-foundation/Tests/schema/iana-registry.rng b/lib/silex/vendor/symfony/http-foundation/Tests/schema/iana-registry.rng new file mode 100644 index 000000000..b9c3ca9d9 --- /dev/null +++ b/lib/silex/vendor/symfony/http-foundation/Tests/schema/iana-registry.rng @@ -0,0 +1,198 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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/lib/silex/vendor/symfony/http-foundation/composer.json b/lib/silex/vendor/symfony/http-foundation/composer.json new file mode 100644 index 000000000..f6c6f2e62 --- /dev/null +++ b/lib/silex/vendor/symfony/http-foundation/composer.json @@ -0,0 +1,38 @@ +{ + "name": "symfony/http-foundation", + "type": "library", + "description": "Symfony HttpFoundation Component", + "keywords": [], + "homepage": "https://symfony.com", + "license": "MIT", + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "require": { + "php": "^5.5.9|>=7.0.8", + "symfony/polyfill-mbstring": "~1.1", + "symfony/polyfill-php70": "~1.6" + }, + "require-dev": { + "symfony/expression-language": "~2.8|~3.0|~4.0" + }, + "autoload": { + "psr-4": { "Symfony\\Component\\HttpFoundation\\": "" }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "minimum-stability": "dev", + "extra": { + "branch-alias": { + "dev-master": "3.4-dev" + } + } +} diff --git a/lib/silex/vendor/symfony/http-foundation/phpunit.xml.dist b/lib/silex/vendor/symfony/http-foundation/phpunit.xml.dist new file mode 100644 index 000000000..c1d61f8bf --- /dev/null +++ b/lib/silex/vendor/symfony/http-foundation/phpunit.xml.dist @@ -0,0 +1,31 @@ + + + + + + + + + + ./Tests/ + + + + + + ./ + + ./Resources + ./Tests + ./vendor + + + + diff --git a/lib/silex/vendor/symfony/http-kernel/.gitignore b/lib/silex/vendor/symfony/http-kernel/.gitignore new file mode 100644 index 000000000..94a6a2528 --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/.gitignore @@ -0,0 +1,5 @@ +vendor/ +composer.lock +phpunit.xml +Tests/Fixtures/cache/ +Tests/Fixtures/logs/ diff --git a/lib/silex/vendor/symfony/http-kernel/Bundle/Bundle.php b/lib/silex/vendor/symfony/http-kernel/Bundle/Bundle.php new file mode 100644 index 000000000..cfa576c36 --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Bundle/Bundle.php @@ -0,0 +1,229 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Bundle; + +use Symfony\Component\DependencyInjection\ContainerAwareTrait; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Container; +use Symfony\Component\Console\Application; +use Symfony\Component\Finder\Finder; +use Symfony\Component\DependencyInjection\Extension\ExtensionInterface; + +/** + * An implementation of BundleInterface that adds a few conventions + * for DependencyInjection extensions and Console commands. + * + * @author Fabien Potencier + */ +abstract class Bundle implements BundleInterface +{ + use ContainerAwareTrait; + + protected $name; + protected $extension; + protected $path; + private $namespace; + + /** + * Boots the Bundle. + */ + public function boot() + { + } + + /** + * Shutdowns the Bundle. + */ + public function shutdown() + { + } + + /** + * Builds the bundle. + * + * It is only ever called once when the cache is empty. + * + * This method can be overridden to register compilation passes, + * other extensions, ... + */ + public function build(ContainerBuilder $container) + { + } + + /** + * Returns the bundle's container extension. + * + * @return ExtensionInterface|null The container extension + * + * @throws \LogicException + */ + public function getContainerExtension() + { + if (null === $this->extension) { + $extension = $this->createContainerExtension(); + + if (null !== $extension) { + if (!$extension instanceof ExtensionInterface) { + throw new \LogicException(sprintf('Extension %s must implement Symfony\Component\DependencyInjection\Extension\ExtensionInterface.', get_class($extension))); + } + + // check naming convention + $basename = preg_replace('/Bundle$/', '', $this->getName()); + $expectedAlias = Container::underscore($basename); + + if ($expectedAlias != $extension->getAlias()) { + throw new \LogicException(sprintf( + 'Users will expect the alias of the default extension of a bundle to be the underscored version of the bundle name ("%s"). You can override "Bundle::getContainerExtension()" if you want to use "%s" or another alias.', + $expectedAlias, $extension->getAlias() + )); + } + + $this->extension = $extension; + } else { + $this->extension = false; + } + } + + if ($this->extension) { + return $this->extension; + } + } + + /** + * Gets the Bundle namespace. + * + * @return string The Bundle namespace + */ + public function getNamespace() + { + if (null === $this->namespace) { + $this->parseClassName(); + } + + return $this->namespace; + } + + /** + * Gets the Bundle directory path. + * + * @return string The Bundle absolute path + */ + public function getPath() + { + if (null === $this->path) { + $reflected = new \ReflectionObject($this); + $this->path = dirname($reflected->getFileName()); + } + + return $this->path; + } + + /** + * Returns the bundle parent name. + * + * @return string|null The Bundle parent name it overrides or null if no parent + */ + public function getParent() + { + } + + /** + * Returns the bundle name (the class short name). + * + * @return string The Bundle name + */ + final public function getName() + { + if (null === $this->name) { + $this->parseClassName(); + } + + return $this->name; + } + + /** + * Finds and registers Commands. + * + * Override this method if your bundle commands do not follow the conventions: + * + * * Commands are in the 'Command' sub-directory + * * Commands extend Symfony\Component\Console\Command\Command + */ + public function registerCommands(Application $application) + { + if (!is_dir($dir = $this->getPath().'/Command')) { + return; + } + + if (!class_exists('Symfony\Component\Finder\Finder')) { + throw new \RuntimeException('You need the symfony/finder component to register bundle commands.'); + } + + $finder = new Finder(); + $finder->files()->name('*Command.php')->in($dir); + + $prefix = $this->getNamespace().'\\Command'; + foreach ($finder as $file) { + $ns = $prefix; + if ($relativePath = $file->getRelativePath()) { + $ns .= '\\'.str_replace('/', '\\', $relativePath); + } + $class = $ns.'\\'.$file->getBasename('.php'); + if ($this->container) { + $commandIds = $this->container->hasParameter('console.command.ids') ? $this->container->getParameter('console.command.ids') : array(); + $alias = 'console.command.'.strtolower(str_replace('\\', '_', $class)); + if (isset($commandIds[$alias]) || $this->container->has($alias)) { + continue; + } + } + $r = new \ReflectionClass($class); + if ($r->isSubclassOf('Symfony\\Component\\Console\\Command\\Command') && !$r->isAbstract() && !$r->getConstructor()->getNumberOfRequiredParameters()) { + @trigger_error(sprintf('Auto-registration of the command "%s" is deprecated since Symfony 3.4 and won\'t be supported in 4.0. Use PSR-4 based service discovery instead.', $class), E_USER_DEPRECATED); + + $application->add($r->newInstance()); + } + } + } + + /** + * Returns the bundle's container extension class. + * + * @return string + */ + protected function getContainerExtensionClass() + { + $basename = preg_replace('/Bundle$/', '', $this->getName()); + + return $this->getNamespace().'\\DependencyInjection\\'.$basename.'Extension'; + } + + /** + * Creates the bundle's container extension. + * + * @return ExtensionInterface|null + */ + protected function createContainerExtension() + { + if (class_exists($class = $this->getContainerExtensionClass())) { + return new $class(); + } + } + + private function parseClassName() + { + $pos = strrpos(static::class, '\\'); + $this->namespace = false === $pos ? '' : substr(static::class, 0, $pos); + if (null === $this->name) { + $this->name = false === $pos ? static::class : substr(static::class, $pos + 1); + } + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/Bundle/BundleInterface.php b/lib/silex/vendor/symfony/http-kernel/Bundle/BundleInterface.php new file mode 100644 index 000000000..14a7f6f4f --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Bundle/BundleInterface.php @@ -0,0 +1,84 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Bundle; + +use Symfony\Component\DependencyInjection\ContainerAwareInterface; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Extension\ExtensionInterface; + +/** + * BundleInterface. + * + * @author Fabien Potencier + */ +interface BundleInterface extends ContainerAwareInterface +{ + /** + * Boots the Bundle. + */ + public function boot(); + + /** + * Shutdowns the Bundle. + */ + public function shutdown(); + + /** + * Builds the bundle. + * + * It is only ever called once when the cache is empty. + */ + public function build(ContainerBuilder $container); + + /** + * Returns the container extension that should be implicitly loaded. + * + * @return ExtensionInterface|null The default extension or null if there is none + */ + public function getContainerExtension(); + + /** + * Returns the bundle name that this bundle overrides. + * + * Despite its name, this method does not imply any parent/child relationship + * between the bundles, just a way to extend and override an existing + * bundle. + * + * @return string The Bundle name it overrides or null if no parent + * + * @deprecated This method is deprecated as of 3.4 and will be removed in 4.0. + */ + public function getParent(); + + /** + * Returns the bundle name (the class short name). + * + * @return string The Bundle name + */ + public function getName(); + + /** + * Gets the Bundle namespace. + * + * @return string The Bundle namespace + */ + public function getNamespace(); + + /** + * Gets the Bundle directory path. + * + * The path should always be returned as a Unix path (with /). + * + * @return string The Bundle absolute path + */ + public function getPath(); +} diff --git a/lib/silex/vendor/symfony/http-kernel/CHANGELOG.md b/lib/silex/vendor/symfony/http-kernel/CHANGELOG.md new file mode 100644 index 000000000..fb29f7696 --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/CHANGELOG.md @@ -0,0 +1,151 @@ +CHANGELOG +========= + +3.4.0 +----- + + * added a minimalist PSR-3 `Logger` class that writes in `stderr` + * made kernels implementing `CompilerPassInterface` able to process the container + * deprecated bundle inheritance + * added `RebootableInterface` and implemented it in `Kernel` + * deprecated commands auto registration + * deprecated `EnvParametersResource` + * added `Symfony\Component\HttpKernel\Client::catchExceptions()` + * deprecated the `ChainCacheClearer::add()` method + * deprecated the `CacheaWarmerAggregate::add()` and `setWarmers()` methods + * made `CacheWarmerAggregate` and `ChainCacheClearer` classes final + * added the possibility to reset the profiler to its initial state + * deprecated data collectors without a `reset()` method + * deprecated implementing `DebugLoggerInterface` without a `clear()` method + +3.3.0 +----- + + * added `kernel.project_dir` and `Kernel::getProjectDir()` + * deprecated `kernel.root_dir` and `Kernel::getRootDir()` + * deprecated `Kernel::getEnvParameters()` + * deprecated the special `SYMFONY__` environment variables + * added the possibility to change the query string parameter used by `UriSigner` + * deprecated `LazyLoadingFragmentHandler::addRendererService()` + * deprecated `Extension::addClassesToCompile()` and `Extension::getClassesToCompile()` + * deprecated `Psr6CacheClearer::addPool()` + +3.2.0 +----- + + * deprecated `DataCollector::varToString()`, use `cloneVar()` instead + * changed surrogate capability name in `AbstractSurrogate::addSurrogateCapability` to 'symfony' + * Added `ControllerArgumentValueResolverPass` + +3.1.0 +----- + * deprecated passing objects as URI attributes to the ESI and SSI renderers + * deprecated `ControllerResolver::getArguments()` + * added `Symfony\Component\HttpKernel\Controller\ArgumentResolverInterface` + * added `Symfony\Component\HttpKernel\Controller\ArgumentResolverInterface` as argument to `HttpKernel` + * added `Symfony\Component\HttpKernel\Controller\ArgumentResolver` + * added `Symfony\Component\HttpKernel\DataCollector\RequestDataCollector::getMethod()` + * added `Symfony\Component\HttpKernel\DataCollector\RequestDataCollector::getRedirect()` + * added the `kernel.controller_arguments` event, triggered after controller arguments have been resolved + +3.0.0 +----- + + * removed `Symfony\Component\HttpKernel\Kernel::init()` + * removed `Symfony\Component\HttpKernel\Kernel::isClassInActiveBundle()` and `Symfony\Component\HttpKernel\KernelInterface::isClassInActiveBundle()` + * removed `Symfony\Component\HttpKernel\Debug\TraceableEventDispatcher::setProfiler()` + * removed `Symfony\Component\HttpKernel\EventListener\FragmentListener::getLocalIpAddresses()` + * removed `Symfony\Component\HttpKernel\EventListener\LocaleListener::setRequest()` + * removed `Symfony\Component\HttpKernel\EventListener\RouterListener::setRequest()` + * removed `Symfony\Component\HttpKernel\EventListener\ProfilerListener::onKernelRequest()` + * removed `Symfony\Component\HttpKernel\Fragment\FragmentHandler::setRequest()` + * removed `Symfony\Component\HttpKernel\HttpCache\Esi::hasSurrogateEsiCapability()` + * removed `Symfony\Component\HttpKernel\HttpCache\Esi::addSurrogateEsiCapability()` + * removed `Symfony\Component\HttpKernel\HttpCache\Esi::needsEsiParsing()` + * removed `Symfony\Component\HttpKernel\HttpCache\HttpCache::getEsi()` + * removed `Symfony\Component\HttpKernel\DependencyInjection\ContainerAwareHttpKernel` + * removed `Symfony\Component\HttpKernel\DependencyInjection\RegisterListenersPass` + * removed `Symfony\Component\HttpKernel\EventListener\ErrorsLoggerListener` + * removed `Symfony\Component\HttpKernel\EventListener\EsiListener` + * removed `Symfony\Component\HttpKernel\HttpCache\EsiResponseCacheStrategy` + * removed `Symfony\Component\HttpKernel\HttpCache\EsiResponseCacheStrategyInterface` + * removed `Symfony\Component\HttpKernel\Log\LoggerInterface` + * removed `Symfony\Component\HttpKernel\Log\NullLogger` + * removed `Symfony\Component\HttpKernel\Profiler::import()` + * removed `Symfony\Component\HttpKernel\Profiler::export()` + +2.8.0 +----- + + * deprecated `Profiler::import` and `Profiler::export` + +2.7.0 +----- + + * added the HTTP status code to profiles + +2.6.0 +----- + + * deprecated `Symfony\Component\HttpKernel\EventListener\ErrorsLoggerListener`, use `Symfony\Component\HttpKernel\EventListener\DebugHandlersListener` instead + * deprecated unused method `Symfony\Component\HttpKernel\Kernel::isClassInActiveBundle` and `Symfony\Component\HttpKernel\KernelInterface::isClassInActiveBundle` + +2.5.0 +----- + + * deprecated `Symfony\Component\HttpKernel\DependencyInjection\RegisterListenersPass`, use `Symfony\Component\EventDispatcher\DependencyInjection\RegisterListenersPass` instead + +2.4.0 +----- + + * added event listeners for the session + * added the KernelEvents::FINISH_REQUEST event + +2.3.0 +----- + + * [BC BREAK] renamed `Symfony\Component\HttpKernel\EventListener\DeprecationLoggerListener` to `Symfony\Component\HttpKernel\EventListener\ErrorsLoggerListener` and changed its constructor + * deprecated `Symfony\Component\HttpKernel\Debug\ErrorHandler`, `Symfony\Component\HttpKernel\Debug\ExceptionHandler`, + `Symfony\Component\HttpKernel\Exception\FatalErrorException` and `Symfony\Component\HttpKernel\Exception\FlattenException` + * deprecated `Symfony\Component\HttpKernel\Kernel::init()` + * added the possibility to specify an id an extra attributes to hinclude tags + * added the collect of data if a controller is a Closure in the Request collector + * pass exceptions from the ExceptionListener to the logger using the logging context to allow for more + detailed messages + +2.2.0 +----- + + * [BC BREAK] the path info for sub-request is now always _fragment (or whatever you configured instead of the default) + * added Symfony\Component\HttpKernel\EventListener\FragmentListener + * added Symfony\Component\HttpKernel\UriSigner + * added Symfony\Component\HttpKernel\FragmentRenderer and rendering strategies (in Symfony\Component\HttpKernel\Fragment\FragmentRendererInterface) + * added Symfony\Component\HttpKernel\DependencyInjection\ContainerAwareHttpKernel + * added ControllerReference to create reference of Controllers (used in the FragmentRenderer class) + * [BC BREAK] renamed TimeDataCollector::getTotalTime() to + TimeDataCollector::getDuration() + * updated the MemoryDataCollector to include the memory used in the + kernel.terminate event listeners + * moved the Stopwatch classes to a new component + * added TraceableControllerResolver + * added TraceableEventDispatcher (removed ContainerAwareTraceableEventDispatcher) + * added support for WinCache opcode cache in ConfigDataCollector + +2.1.0 +----- + + * [BC BREAK] the charset is now configured via the Kernel::getCharset() method + * [BC BREAK] the current locale for the user is not stored anymore in the session + * added the HTTP method to the profiler storage + * updated all listeners to implement EventSubscriberInterface + * added TimeDataCollector + * added ContainerAwareTraceableEventDispatcher + * moved TraceableEventDispatcherInterface to the EventDispatcher component + * added RouterListener, LocaleListener, and StreamedResponseListener + * added CacheClearerInterface (and ChainCacheClearer) + * added a kernel.terminate event (via TerminableInterface and PostResponseEvent) + * added a Stopwatch class + * added WarmableInterface + * improved extensibility between bundles + * added profiler storages for Memcache(d), File-based, MongoDB, Redis + * moved Filesystem class to its own component diff --git a/lib/silex/vendor/symfony/http-kernel/CacheClearer/CacheClearerInterface.php b/lib/silex/vendor/symfony/http-kernel/CacheClearer/CacheClearerInterface.php new file mode 100644 index 000000000..675c58423 --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/CacheClearer/CacheClearerInterface.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\CacheClearer; + +/** + * CacheClearerInterface. + * + * @author Dustin Dobervich + */ +interface CacheClearerInterface +{ + /** + * Clears any caches necessary. + * + * @param string $cacheDir The cache directory + */ + public function clear($cacheDir); +} diff --git a/lib/silex/vendor/symfony/http-kernel/CacheClearer/ChainCacheClearer.php b/lib/silex/vendor/symfony/http-kernel/CacheClearer/ChainCacheClearer.php new file mode 100644 index 000000000..8ee4275b1 --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/CacheClearer/ChainCacheClearer.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\HttpKernel\CacheClearer; + +/** + * ChainCacheClearer. + * + * @author Dustin Dobervich + * + * @final since version 3.4 + */ +class ChainCacheClearer implements CacheClearerInterface +{ + protected $clearers; + + /** + * Constructs a new instance of ChainCacheClearer. + * + * @param array $clearers The initial clearers + */ + public function __construct($clearers = array()) + { + $this->clearers = $clearers; + } + + /** + * {@inheritdoc} + */ + public function clear($cacheDir) + { + foreach ($this->clearers as $clearer) { + $clearer->clear($cacheDir); + } + } + + /** + * Adds a cache clearer to the aggregate. + * + * @deprecated since version 3.4, to be removed in 4.0, inject the list of clearers as a constructor argument instead. + */ + public function add(CacheClearerInterface $clearer) + { + @trigger_error(sprintf('The "%s()" method is deprecated since Symfony 3.4 and will be removed in 4.0, inject the list of clearers as a constructor argument instead.', __METHOD__), E_USER_DEPRECATED); + + $this->clearers[] = $clearer; + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/CacheClearer/Psr6CacheClearer.php b/lib/silex/vendor/symfony/http-kernel/CacheClearer/Psr6CacheClearer.php new file mode 100644 index 000000000..d413d2c9d --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/CacheClearer/Psr6CacheClearer.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\HttpKernel\CacheClearer; + +use Psr\Cache\CacheItemPoolInterface; + +/** + * @author Nicolas Grekas + */ +class Psr6CacheClearer implements CacheClearerInterface +{ + private $pools = array(); + + public function __construct(array $pools = array()) + { + $this->pools = $pools; + } + + public function addPool(CacheItemPoolInterface $pool) + { + @trigger_error(sprintf('The %s() method is deprecated since Symfony 3.3 and will be removed in 4.0. Pass an array of pools indexed by name to the constructor instead.', __METHOD__), E_USER_DEPRECATED); + + $this->pools[] = $pool; + } + + public function hasPool($name) + { + return isset($this->pools[$name]); + } + + public function clearPool($name) + { + if (!isset($this->pools[$name])) { + throw new \InvalidArgumentException(sprintf('Cache pool not found: %s.', $name)); + } + + return $this->pools[$name]->clear(); + } + + /** + * {@inheritdoc} + */ + public function clear($cacheDir) + { + foreach ($this->pools as $pool) { + $pool->clear(); + } + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/CacheWarmer/CacheWarmer.php b/lib/silex/vendor/symfony/http-kernel/CacheWarmer/CacheWarmer.php new file mode 100644 index 000000000..dba35a639 --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/CacheWarmer/CacheWarmer.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\HttpKernel\CacheWarmer; + +/** + * Abstract cache warmer that knows how to write a file to the cache. + * + * @author Fabien Potencier + */ +abstract class CacheWarmer implements CacheWarmerInterface +{ + protected function writeCacheFile($file, $content) + { + $tmpFile = @tempnam(dirname($file), basename($file)); + if (false !== @file_put_contents($tmpFile, $content) && @rename($tmpFile, $file)) { + @chmod($file, 0666 & ~umask()); + + return; + } + + throw new \RuntimeException(sprintf('Failed to write cache file "%s".', $file)); + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/CacheWarmer/CacheWarmerAggregate.php b/lib/silex/vendor/symfony/http-kernel/CacheWarmer/CacheWarmerAggregate.php new file mode 100644 index 000000000..ca3911ed5 --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/CacheWarmer/CacheWarmerAggregate.php @@ -0,0 +1,90 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\CacheWarmer; + +/** + * Aggregates several cache warmers into a single one. + * + * @author Fabien Potencier + * + * @final since version 3.4 + */ +class CacheWarmerAggregate implements CacheWarmerInterface +{ + protected $warmers = array(); + protected $optionalsEnabled = false; + private $triggerDeprecation = false; + + public function __construct($warmers = array()) + { + foreach ($warmers as $warmer) { + $this->add($warmer); + } + $this->triggerDeprecation = true; + } + + public function enableOptionalWarmers() + { + $this->optionalsEnabled = true; + } + + /** + * Warms up the cache. + * + * @param string $cacheDir The cache directory + */ + public function warmUp($cacheDir) + { + foreach ($this->warmers as $warmer) { + if (!$this->optionalsEnabled && $warmer->isOptional()) { + continue; + } + + $warmer->warmUp($cacheDir); + } + } + + /** + * Checks whether this warmer is optional or not. + * + * @return bool always false + */ + public function isOptional() + { + return false; + } + + /** + * @deprecated since version 3.4, to be removed in 4.0, inject the list of clearers as a constructor argument instead. + */ + public function setWarmers(array $warmers) + { + @trigger_error(sprintf('The "%s()" method is deprecated since Symfony 3.4 and will be removed in 4.0, inject the list of clearers as a constructor argument instead.', __METHOD__), E_USER_DEPRECATED); + + $this->warmers = array(); + foreach ($warmers as $warmer) { + $this->add($warmer); + } + } + + /** + * @deprecated since version 3.4, to be removed in 4.0, inject the list of clearers as a constructor argument instead. + */ + public function add(CacheWarmerInterface $warmer) + { + if ($this->triggerDeprecation) { + @trigger_error(sprintf('The "%s()" method is deprecated since Symfony 3.4 and will be removed in 4.0, inject the list of clearers as a constructor argument instead.', __METHOD__), E_USER_DEPRECATED); + } + + $this->warmers[] = $warmer; + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/CacheWarmer/CacheWarmerInterface.php b/lib/silex/vendor/symfony/http-kernel/CacheWarmer/CacheWarmerInterface.php new file mode 100644 index 000000000..8fece5e95 --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/CacheWarmer/CacheWarmerInterface.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\HttpKernel\CacheWarmer; + +/** + * Interface for classes able to warm up the cache. + * + * @author Fabien Potencier + */ +interface CacheWarmerInterface extends WarmableInterface +{ + /** + * Checks whether this warmer is optional or not. + * + * Optional warmers can be ignored on certain conditions. + * + * A warmer should return true if the cache can be + * generated incrementally and on-demand. + * + * @return bool true if the warmer is optional, false otherwise + */ + public function isOptional(); +} diff --git a/lib/silex/vendor/symfony/http-kernel/CacheWarmer/WarmableInterface.php b/lib/silex/vendor/symfony/http-kernel/CacheWarmer/WarmableInterface.php new file mode 100644 index 000000000..25d8ee8f6 --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/CacheWarmer/WarmableInterface.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\CacheWarmer; + +/** + * Interface for classes that support warming their cache. + * + * @author Fabien Potencier + */ +interface WarmableInterface +{ + /** + * Warms up the cache. + * + * @param string $cacheDir The cache directory + */ + public function warmUp($cacheDir); +} diff --git a/lib/silex/vendor/symfony/http-kernel/Client.php b/lib/silex/vendor/symfony/http-kernel/Client.php new file mode 100644 index 000000000..7979722d1 --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Client.php @@ -0,0 +1,205 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel; + +use Symfony\Component\BrowserKit\Client as BaseClient; +use Symfony\Component\BrowserKit\Request as DomRequest; +use Symfony\Component\BrowserKit\Response as DomResponse; +use Symfony\Component\BrowserKit\History; +use Symfony\Component\BrowserKit\CookieJar; +use Symfony\Component\HttpFoundation\File\UploadedFile; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +/** + * Client simulates a browser and makes requests to a Kernel object. + * + * @author Fabien Potencier + * + * @method Request|null getRequest() A Request instance + * @method Response|null getResponse() A Response instance + */ +class Client extends BaseClient +{ + protected $kernel; + private $catchExceptions = true; + + /** + * @param HttpKernelInterface $kernel An HttpKernel instance + * @param array $server The server parameters (equivalent of $_SERVER) + * @param History $history A History instance to store the browser history + * @param CookieJar $cookieJar A CookieJar instance to store the cookies + */ + public function __construct(HttpKernelInterface $kernel, array $server = array(), History $history = null, CookieJar $cookieJar = null) + { + // These class properties must be set before calling the parent constructor, as it may depend on it. + $this->kernel = $kernel; + $this->followRedirects = false; + + parent::__construct($server, $history, $cookieJar); + } + + /** + * Sets whether to catch exceptions when the kernel is handling a request. + * + * @param bool $catchExceptions Whether to catch exceptions + */ + public function catchExceptions($catchExceptions) + { + $this->catchExceptions = $catchExceptions; + } + + /** + * Makes a request. + * + * @return Response A Response instance + */ + protected function doRequest($request) + { + $response = $this->kernel->handle($request, HttpKernelInterface::MASTER_REQUEST, $this->catchExceptions); + + if ($this->kernel instanceof TerminableInterface) { + $this->kernel->terminate($request, $response); + } + + return $response; + } + + /** + * Returns the script to execute when the request must be insulated. + * + * @return string + */ + protected function getScript($request) + { + $kernel = str_replace("'", "\\'", serialize($this->kernel)); + $request = str_replace("'", "\\'", serialize($request)); + $errorReporting = error_reporting(); + + $requires = ''; + foreach (get_declared_classes() as $class) { + if (0 === strpos($class, 'ComposerAutoloaderInit')) { + $r = new \ReflectionClass($class); + $file = dirname(dirname($r->getFileName())).'/autoload.php'; + if (file_exists($file)) { + $requires .= "require_once '".str_replace("'", "\\'", $file)."';\n"; + } + } + } + + if (!$requires) { + throw new \RuntimeException('Composer autoloader not found.'); + } + + $code = <<getHandleScript(); + } + + protected function getHandleScript() + { + return <<<'EOF' +$response = $kernel->handle($request); + +if ($kernel instanceof Symfony\Component\HttpKernel\TerminableInterface) { + $kernel->terminate($request, $response); +} + +echo serialize($response); +EOF; + } + + /** + * Converts the BrowserKit request to a HttpKernel request. + * + * @return Request A Request instance + */ + protected function filterRequest(DomRequest $request) + { + $httpRequest = Request::create($request->getUri(), $request->getMethod(), $request->getParameters(), $request->getCookies(), $request->getFiles(), $request->getServer(), $request->getContent()); + + foreach ($this->filterFiles($httpRequest->files->all()) as $key => $value) { + $httpRequest->files->set($key, $value); + } + + return $httpRequest; + } + + /** + * Filters an array of files. + * + * This method created test instances of UploadedFile so that the move() + * method can be called on those instances. + * + * If the size of a file is greater than the allowed size (from php.ini) then + * an invalid UploadedFile is returned with an error set to UPLOAD_ERR_INI_SIZE. + * + * @see UploadedFile + * + * @return array An array with all uploaded files marked as already moved + */ + protected function filterFiles(array $files) + { + $filtered = array(); + foreach ($files as $key => $value) { + if (is_array($value)) { + $filtered[$key] = $this->filterFiles($value); + } elseif ($value instanceof UploadedFile) { + if ($value->isValid() && $value->getSize() > UploadedFile::getMaxFilesize()) { + $filtered[$key] = new UploadedFile( + '', + $value->getClientOriginalName(), + $value->getClientMimeType(), + 0, + UPLOAD_ERR_INI_SIZE, + true + ); + } else { + $filtered[$key] = new UploadedFile( + $value->getPathname(), + $value->getClientOriginalName(), + $value->getClientMimeType(), + $value->getClientSize(), + $value->getError(), + true + ); + } + } + } + + return $filtered; + } + + /** + * Converts the HttpKernel response to a BrowserKit response. + * + * @return DomResponse A DomResponse instance + */ + protected function filterResponse($response) + { + // this is needed to support StreamedResponse + ob_start(); + $response->sendContent(); + $content = ob_get_clean(); + + return new DomResponse($content, $response->getStatusCode(), $response->headers->all()); + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/Config/EnvParametersResource.php b/lib/silex/vendor/symfony/http-kernel/Config/EnvParametersResource.php new file mode 100644 index 000000000..f8d2a72b2 --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Config/EnvParametersResource.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\HttpKernel\Config; + +use Symfony\Component\Config\Resource\SelfCheckingResourceInterface; + +/** + * EnvParametersResource represents resources stored in prefixed environment variables. + * + * @author Chris Wilkinson + * + * @deprecated since version 3.4, to be removed in 4.0 + */ +class EnvParametersResource implements SelfCheckingResourceInterface, \Serializable +{ + /** + * @var string + */ + private $prefix; + + /** + * @var string + */ + private $variables; + + /** + * @param string $prefix + */ + public function __construct($prefix) + { + $this->prefix = $prefix; + $this->variables = $this->findVariables(); + } + + /** + * {@inheritdoc} + */ + public function __toString() + { + return serialize($this->getResource()); + } + + /** + * @return array An array with two keys: 'prefix' for the prefix used and 'variables' containing all the variables watched by this resource + */ + public function getResource() + { + return array('prefix' => $this->prefix, 'variables' => $this->variables); + } + + /** + * {@inheritdoc} + */ + public function isFresh($timestamp) + { + return $this->findVariables() === $this->variables; + } + + public function serialize() + { + return serialize(array('prefix' => $this->prefix, 'variables' => $this->variables)); + } + + public function unserialize($serialized) + { + if (\PHP_VERSION_ID >= 70000) { + $unserialized = unserialize($serialized, array('allowed_classes' => false)); + } else { + $unserialized = unserialize($serialized); + } + + $this->prefix = $unserialized['prefix']; + $this->variables = $unserialized['variables']; + } + + private function findVariables() + { + $variables = array(); + + foreach ($_SERVER as $key => $value) { + if (0 === strpos($key, $this->prefix)) { + $variables[$key] = $value; + } + } + + ksort($variables); + + return $variables; + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/Config/FileLocator.php b/lib/silex/vendor/symfony/http-kernel/Config/FileLocator.php new file mode 100644 index 000000000..fb1f913bd --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Config/FileLocator.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\HttpKernel\Config; + +use Symfony\Component\Config\FileLocator as BaseFileLocator; +use Symfony\Component\HttpKernel\KernelInterface; + +/** + * FileLocator uses the KernelInterface to locate resources in bundles. + * + * @author Fabien Potencier + */ +class FileLocator extends BaseFileLocator +{ + private $kernel; + private $path; + + /** + * @param KernelInterface $kernel A KernelInterface instance + * @param null|string $path The path the global resource directory + * @param array $paths An array of paths where to look for resources + */ + public function __construct(KernelInterface $kernel, $path = null, array $paths = array()) + { + $this->kernel = $kernel; + if (null !== $path) { + $this->path = $path; + $paths[] = $path; + } + + parent::__construct($paths); + } + + /** + * {@inheritdoc} + */ + public function locate($file, $currentPath = null, $first = true) + { + if (isset($file[0]) && '@' === $file[0]) { + return $this->kernel->locateResource($file, $this->path, $first); + } + + return parent::locate($file, $currentPath, $first); + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/Controller/ArgumentResolver.php b/lib/silex/vendor/symfony/http-kernel/Controller/ArgumentResolver.php new file mode 100644 index 000000000..2c17125c5 --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Controller/ArgumentResolver.php @@ -0,0 +1,94 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Controller; + +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpKernel\Controller\ArgumentResolver\DefaultValueResolver; +use Symfony\Component\HttpKernel\Controller\ArgumentResolver\RequestAttributeValueResolver; +use Symfony\Component\HttpKernel\Controller\ArgumentResolver\RequestValueResolver; +use Symfony\Component\HttpKernel\Controller\ArgumentResolver\SessionValueResolver; +use Symfony\Component\HttpKernel\Controller\ArgumentResolver\VariadicValueResolver; +use Symfony\Component\HttpKernel\ControllerMetadata\ArgumentMetadataFactory; +use Symfony\Component\HttpKernel\ControllerMetadata\ArgumentMetadataFactoryInterface; + +/** + * Responsible for resolving the arguments passed to an action. + * + * @author Iltar van der Berg + */ +final class ArgumentResolver implements ArgumentResolverInterface +{ + private $argumentMetadataFactory; + + /** + * @var iterable|ArgumentValueResolverInterface[] + */ + private $argumentValueResolvers; + + public function __construct(ArgumentMetadataFactoryInterface $argumentMetadataFactory = null, $argumentValueResolvers = array()) + { + $this->argumentMetadataFactory = $argumentMetadataFactory ?: new ArgumentMetadataFactory(); + $this->argumentValueResolvers = $argumentValueResolvers ?: self::getDefaultArgumentValueResolvers(); + } + + /** + * {@inheritdoc} + */ + public function getArguments(Request $request, $controller) + { + $arguments = array(); + + foreach ($this->argumentMetadataFactory->createArgumentMetadata($controller) as $metadata) { + foreach ($this->argumentValueResolvers as $resolver) { + if (!$resolver->supports($request, $metadata)) { + continue; + } + + $resolved = $resolver->resolve($request, $metadata); + + if (!$resolved instanceof \Generator) { + throw new \InvalidArgumentException(sprintf('%s::resolve() must yield at least one value.', get_class($resolver))); + } + + foreach ($resolved as $append) { + $arguments[] = $append; + } + + // continue to the next controller argument + continue 2; + } + + $representative = $controller; + + if (is_array($representative)) { + $representative = sprintf('%s::%s()', get_class($representative[0]), $representative[1]); + } elseif (is_object($representative)) { + $representative = get_class($representative); + } + + throw new \RuntimeException(sprintf('Controller "%s" requires that you provide a value for the "$%s" argument. Either the argument is nullable and no null value has been provided, no default value has been provided or because there is a non optional argument after this one.', $representative, $metadata->getName())); + } + + return $arguments; + } + + public static function getDefaultArgumentValueResolvers() + { + return array( + new RequestAttributeValueResolver(), + new RequestValueResolver(), + new SessionValueResolver(), + new DefaultValueResolver(), + new VariadicValueResolver(), + ); + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/Controller/ArgumentResolver/DefaultValueResolver.php b/lib/silex/vendor/symfony/http-kernel/Controller/ArgumentResolver/DefaultValueResolver.php new file mode 100644 index 000000000..e58fd3ab2 --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Controller/ArgumentResolver/DefaultValueResolver.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\HttpKernel\Controller\ArgumentResolver; + +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpKernel\Controller\ArgumentValueResolverInterface; +use Symfony\Component\HttpKernel\ControllerMetadata\ArgumentMetadata; + +/** + * Yields the default value defined in the action signature when no value has been given. + * + * @author Iltar van der Berg + */ +final class DefaultValueResolver implements ArgumentValueResolverInterface +{ + /** + * {@inheritdoc} + */ + public function supports(Request $request, ArgumentMetadata $argument) + { + return $argument->hasDefaultValue() || (null !== $argument->getType() && $argument->isNullable() && !$argument->isVariadic()); + } + + /** + * {@inheritdoc} + */ + public function resolve(Request $request, ArgumentMetadata $argument) + { + yield $argument->hasDefaultValue() ? $argument->getDefaultValue() : null; + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/Controller/ArgumentResolver/RequestAttributeValueResolver.php b/lib/silex/vendor/symfony/http-kernel/Controller/ArgumentResolver/RequestAttributeValueResolver.php new file mode 100644 index 000000000..05be372d8 --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Controller/ArgumentResolver/RequestAttributeValueResolver.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\HttpKernel\Controller\ArgumentResolver; + +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpKernel\Controller\ArgumentValueResolverInterface; +use Symfony\Component\HttpKernel\ControllerMetadata\ArgumentMetadata; + +/** + * Yields a non-variadic argument's value from the request attributes. + * + * @author Iltar van der Berg + */ +final class RequestAttributeValueResolver implements ArgumentValueResolverInterface +{ + /** + * {@inheritdoc} + */ + public function supports(Request $request, ArgumentMetadata $argument) + { + return !$argument->isVariadic() && $request->attributes->has($argument->getName()); + } + + /** + * {@inheritdoc} + */ + public function resolve(Request $request, ArgumentMetadata $argument) + { + yield $request->attributes->get($argument->getName()); + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/Controller/ArgumentResolver/RequestValueResolver.php b/lib/silex/vendor/symfony/http-kernel/Controller/ArgumentResolver/RequestValueResolver.php new file mode 100644 index 000000000..2a5060a61 --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Controller/ArgumentResolver/RequestValueResolver.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\HttpKernel\Controller\ArgumentResolver; + +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpKernel\Controller\ArgumentValueResolverInterface; +use Symfony\Component\HttpKernel\ControllerMetadata\ArgumentMetadata; + +/** + * Yields the same instance as the request object passed along. + * + * @author Iltar van der Berg + */ +final class RequestValueResolver implements ArgumentValueResolverInterface +{ + /** + * {@inheritdoc} + */ + public function supports(Request $request, ArgumentMetadata $argument) + { + return Request::class === $argument->getType() || is_subclass_of($argument->getType(), Request::class); + } + + /** + * {@inheritdoc} + */ + public function resolve(Request $request, ArgumentMetadata $argument) + { + yield $request; + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/Controller/ArgumentResolver/ServiceValueResolver.php b/lib/silex/vendor/symfony/http-kernel/Controller/ArgumentResolver/ServiceValueResolver.php new file mode 100644 index 000000000..c55564c04 --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Controller/ArgumentResolver/ServiceValueResolver.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\HttpKernel\Controller\ArgumentResolver; + +use Psr\Container\ContainerInterface; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpKernel\Controller\ArgumentValueResolverInterface; +use Symfony\Component\HttpKernel\ControllerMetadata\ArgumentMetadata; + +/** + * Yields a service keyed by _controller and argument name. + * + * @author Nicolas Grekas + */ +final class ServiceValueResolver implements ArgumentValueResolverInterface +{ + private $container; + + public function __construct(ContainerInterface $container) + { + $this->container = $container; + } + + /** + * {@inheritdoc} + */ + public function supports(Request $request, ArgumentMetadata $argument) + { + $controller = $request->attributes->get('_controller'); + + if (\is_array($controller) && \is_callable($controller, true) && \is_string($controller[0])) { + $controller = $controller[0].'::'.$controller[1]; + } + + return \is_string($controller) && $this->container->has($controller) && $this->container->get($controller)->has($argument->getName()); + } + + /** + * {@inheritdoc} + */ + public function resolve(Request $request, ArgumentMetadata $argument) + { + if (\is_array($controller = $request->attributes->get('_controller'))) { + $controller = $controller[0].'::'.$controller[1]; + } + + yield $this->container->get($controller)->get($argument->getName()); + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/Controller/ArgumentResolver/SessionValueResolver.php b/lib/silex/vendor/symfony/http-kernel/Controller/ArgumentResolver/SessionValueResolver.php new file mode 100644 index 000000000..9e656d281 --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Controller/ArgumentResolver/SessionValueResolver.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\HttpKernel\Controller\ArgumentResolver; + +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Session\SessionInterface; +use Symfony\Component\HttpKernel\Controller\ArgumentValueResolverInterface; +use Symfony\Component\HttpKernel\ControllerMetadata\ArgumentMetadata; + +/** + * Yields the Session. + * + * @author Iltar van der Berg + */ +final class SessionValueResolver implements ArgumentValueResolverInterface +{ + /** + * {@inheritdoc} + */ + public function supports(Request $request, ArgumentMetadata $argument) + { + $type = $argument->getType(); + if (SessionInterface::class !== $type && !is_subclass_of($type, SessionInterface::class)) { + return false; + } + + return $request->getSession() instanceof $type; + } + + /** + * {@inheritdoc} + */ + public function resolve(Request $request, ArgumentMetadata $argument) + { + yield $request->getSession(); + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/Controller/ArgumentResolver/VariadicValueResolver.php b/lib/silex/vendor/symfony/http-kernel/Controller/ArgumentResolver/VariadicValueResolver.php new file mode 100644 index 000000000..56ae5f191 --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Controller/ArgumentResolver/VariadicValueResolver.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\HttpKernel\Controller\ArgumentResolver; + +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpKernel\Controller\ArgumentValueResolverInterface; +use Symfony\Component\HttpKernel\ControllerMetadata\ArgumentMetadata; + +/** + * Yields a variadic argument's values from the request attributes. + * + * @author Iltar van der Berg + */ +final class VariadicValueResolver implements ArgumentValueResolverInterface +{ + /** + * {@inheritdoc} + */ + public function supports(Request $request, ArgumentMetadata $argument) + { + return $argument->isVariadic() && $request->attributes->has($argument->getName()); + } + + /** + * {@inheritdoc} + */ + public function resolve(Request $request, ArgumentMetadata $argument) + { + $values = $request->attributes->get($argument->getName()); + + if (!is_array($values)) { + throw new \InvalidArgumentException(sprintf('The action argument "...$%1$s" is required to be an array, the request attribute "%1$s" contains a type of "%2$s" instead.', $argument->getName(), gettype($values))); + } + + foreach ($values as $value) { + yield $value; + } + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/Controller/ArgumentResolverInterface.php b/lib/silex/vendor/symfony/http-kernel/Controller/ArgumentResolverInterface.php new file mode 100644 index 000000000..5c5123096 --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Controller/ArgumentResolverInterface.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\HttpKernel\Controller; + +use Symfony\Component\HttpFoundation\Request; + +/** + * An ArgumentResolverInterface instance knows how to determine the + * arguments for a specific action. + * + * @author Fabien Potencier + */ +interface ArgumentResolverInterface +{ + /** + * Returns the arguments to pass to the controller. + * + * @param Request $request + * @param callable $controller + * + * @return array An array of arguments to pass to the controller + * + * @throws \RuntimeException When no value could be provided for a required argument + */ + public function getArguments(Request $request, $controller); +} diff --git a/lib/silex/vendor/symfony/http-kernel/Controller/ArgumentValueResolverInterface.php b/lib/silex/vendor/symfony/http-kernel/Controller/ArgumentValueResolverInterface.php new file mode 100644 index 000000000..fd7b09ecf --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Controller/ArgumentValueResolverInterface.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\HttpKernel\Controller; + +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpKernel\ControllerMetadata\ArgumentMetadata; + +/** + * Responsible for resolving the value of an argument based on its metadata. + * + * @author Iltar van der Berg + */ +interface ArgumentValueResolverInterface +{ + /** + * Whether this resolver can resolve the value for the given ArgumentMetadata. + * + * @param Request $request + * @param ArgumentMetadata $argument + * + * @return bool + */ + public function supports(Request $request, ArgumentMetadata $argument); + + /** + * Returns the possible value(s). + * + * @param Request $request + * @param ArgumentMetadata $argument + * + * @return \Generator + */ + public function resolve(Request $request, ArgumentMetadata $argument); +} diff --git a/lib/silex/vendor/symfony/http-kernel/Controller/ContainerControllerResolver.php b/lib/silex/vendor/symfony/http-kernel/Controller/ContainerControllerResolver.php new file mode 100644 index 000000000..186583d7b --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Controller/ContainerControllerResolver.php @@ -0,0 +1,121 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Controller; + +use Psr\Container\ContainerInterface; +use Psr\Log\LoggerInterface; +use Symfony\Component\DependencyInjection\Container; +use Symfony\Component\HttpFoundation\Request; + +/** + * A controller resolver searching for a controller in a psr-11 container when using the "service:method" notation. + * + * @author Fabien Potencier + * @author Maxime Steinhausser + */ +class ContainerControllerResolver extends ControllerResolver +{ + protected $container; + + public function __construct(ContainerInterface $container, LoggerInterface $logger = null) + { + $this->container = $container; + + parent::__construct($logger); + } + + /** + * {@inheritdoc} + */ + public function getController(Request $request) + { + $controller = parent::getController($request); + + if (is_array($controller) && isset($controller[0]) && is_string($controller[0]) && $this->container->has($controller[0])) { + $controller[0] = $this->instantiateController($controller[0]); + } + + return $controller; + } + + /** + * Returns a callable for the given controller. + * + * @param string $controller A Controller string + * + * @return mixed A PHP callable + * + * @throws \LogicException When the name could not be parsed + * @throws \InvalidArgumentException When the controller class does not exist + */ + protected function createController($controller) + { + if (false !== strpos($controller, '::')) { + return parent::createController($controller); + } + + $method = null; + if (1 == substr_count($controller, ':')) { + // controller in the "service:method" notation + list($controller, $method) = explode(':', $controller, 2); + } + + if (!$this->container->has($controller)) { + $this->throwExceptionIfControllerWasRemoved($controller); + + throw new \LogicException(sprintf('Controller not found: service "%s" does not exist.', $controller)); + } + + $service = $this->container->get($controller); + if (null !== $method) { + return array($service, $method); + } + + if (!method_exists($service, '__invoke')) { + throw new \LogicException(sprintf('Controller "%s" cannot be called without a method name. Did you forget an "__invoke" method?', $controller)); + } + + return $service; + } + + /** + * {@inheritdoc} + */ + protected function instantiateController($class) + { + if ($this->container->has($class)) { + return $this->container->get($class); + } + + try { + return parent::instantiateController($class); + } catch (\ArgumentCountError $e) { + } catch (\ErrorException $e) { + } catch (\TypeError $e) { + } + + $this->throwExceptionIfControllerWasRemoved($class, $e); + + throw $e; + } + + /** + * @param string $controller + * @param \Exception|\Throwable|null $previous + */ + private function throwExceptionIfControllerWasRemoved($controller, $previous = null) + { + if ($this->container instanceof Container && isset($this->container->getRemovedIds()[$controller])) { + throw new \LogicException(sprintf('Controller "%s" cannot be fetched from the container because it is private. Did you forget to tag the service with "controller.service_arguments"?', $controller), 0, $previous); + } + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/Controller/ControllerReference.php b/lib/silex/vendor/symfony/http-kernel/Controller/ControllerReference.php new file mode 100644 index 000000000..fae4e7fa4 --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Controller/ControllerReference.php @@ -0,0 +1,44 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Controller; + +use Symfony\Component\HttpKernel\Fragment\FragmentRendererInterface; + +/** + * Acts as a marker and a data holder for a Controller. + * + * Some methods in Symfony accept both a URI (as a string) or a controller as + * an argument. In the latter case, instead of passing an array representing + * the controller, you can use an instance of this class. + * + * @author Fabien Potencier + * + * @see FragmentRendererInterface + */ +class ControllerReference +{ + public $controller; + public $attributes = array(); + public $query = array(); + + /** + * @param string $controller The controller name + * @param array $attributes An array of parameters to add to the Request attributes + * @param array $query An array of parameters to add to the Request query string + */ + public function __construct($controller, array $attributes = array(), array $query = array()) + { + $this->controller = $controller; + $this->attributes = $attributes; + $this->query = $query; + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/Controller/ControllerResolver.php b/lib/silex/vendor/symfony/http-kernel/Controller/ControllerResolver.php new file mode 100644 index 000000000..b9d9f9fa2 --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Controller/ControllerResolver.php @@ -0,0 +1,258 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Controller; + +use Psr\Log\LoggerInterface; +use Symfony\Component\HttpFoundation\Request; + +/** + * This implementation uses the '_controller' request attribute to determine + * the controller to execute and uses the request attributes to determine + * the controller method arguments. + * + * @author Fabien Potencier + */ +class ControllerResolver implements ArgumentResolverInterface, ControllerResolverInterface +{ + private $logger; + + /** + * If the ...$arg functionality is available. + * + * Requires at least PHP 5.6.0 or HHVM 3.9.1 + * + * @var bool + */ + private $supportsVariadic; + + /** + * If scalar types exists. + * + * @var bool + */ + private $supportsScalarTypes; + + public function __construct(LoggerInterface $logger = null) + { + $this->logger = $logger; + + $this->supportsVariadic = method_exists('ReflectionParameter', 'isVariadic'); + $this->supportsScalarTypes = method_exists('ReflectionParameter', 'getType'); + } + + /** + * {@inheritdoc} + * + * This method looks for a '_controller' request attribute that represents + * the controller name (a string like ClassName::MethodName). + */ + public function getController(Request $request) + { + if (!$controller = $request->attributes->get('_controller')) { + if (null !== $this->logger) { + $this->logger->warning('Unable to look for the controller as the "_controller" parameter is missing.'); + } + + return false; + } + + if (is_array($controller)) { + return $controller; + } + + if (is_object($controller)) { + if (method_exists($controller, '__invoke')) { + return $controller; + } + + throw new \InvalidArgumentException(sprintf('Controller "%s" for URI "%s" is not callable.', get_class($controller), $request->getPathInfo())); + } + + if (false === strpos($controller, ':')) { + if (method_exists($controller, '__invoke')) { + return $this->instantiateController($controller); + } elseif (function_exists($controller)) { + return $controller; + } + } + + $callable = $this->createController($controller); + + if (!is_callable($callable)) { + throw new \InvalidArgumentException(sprintf('The controller for URI "%s" is not callable. %s', $request->getPathInfo(), $this->getControllerError($callable))); + } + + return $callable; + } + + /** + * {@inheritdoc} + * + * @deprecated This method is deprecated as of 3.1 and will be removed in 4.0. Implement the ArgumentResolverInterface and inject it in the HttpKernel instead. + */ + public function getArguments(Request $request, $controller) + { + @trigger_error(sprintf('%s is deprecated as of 3.1 and will be removed in 4.0. Implement the %s and inject it in the HttpKernel instead.', __METHOD__, ArgumentResolverInterface::class), E_USER_DEPRECATED); + + if (is_array($controller)) { + $r = new \ReflectionMethod($controller[0], $controller[1]); + } elseif (is_object($controller) && !$controller instanceof \Closure) { + $r = new \ReflectionObject($controller); + $r = $r->getMethod('__invoke'); + } else { + $r = new \ReflectionFunction($controller); + } + + return $this->doGetArguments($request, $controller, $r->getParameters()); + } + + /** + * @param Request $request + * @param callable $controller + * @param \ReflectionParameter[] $parameters + * + * @return array The arguments to use when calling the action + * + * @deprecated This method is deprecated as of 3.1 and will be removed in 4.0. Implement the ArgumentResolverInterface and inject it in the HttpKernel instead. + */ + protected function doGetArguments(Request $request, $controller, array $parameters) + { + @trigger_error(sprintf('%s is deprecated as of 3.1 and will be removed in 4.0. Implement the %s and inject it in the HttpKernel instead.', __METHOD__, ArgumentResolverInterface::class), E_USER_DEPRECATED); + + $attributes = $request->attributes->all(); + $arguments = array(); + foreach ($parameters as $param) { + if (array_key_exists($param->name, $attributes)) { + if ($this->supportsVariadic && $param->isVariadic() && is_array($attributes[$param->name])) { + $arguments = array_merge($arguments, array_values($attributes[$param->name])); + } else { + $arguments[] = $attributes[$param->name]; + } + } elseif ($param->getClass() && $param->getClass()->isInstance($request)) { + $arguments[] = $request; + } elseif ($param->isDefaultValueAvailable()) { + $arguments[] = $param->getDefaultValue(); + } elseif ($this->supportsScalarTypes && $param->hasType() && $param->allowsNull()) { + $arguments[] = null; + } else { + if (is_array($controller)) { + $repr = sprintf('%s::%s()', get_class($controller[0]), $controller[1]); + } elseif (is_object($controller)) { + $repr = get_class($controller); + } else { + $repr = $controller; + } + + throw new \RuntimeException(sprintf('Controller "%s" requires that you provide a value for the "$%s" argument (because there is no default value or because there is a non optional argument after this one).', $repr, $param->name)); + } + } + + return $arguments; + } + + /** + * Returns a callable for the given controller. + * + * @param string $controller A Controller string + * + * @return callable A PHP callable + * + * @throws \InvalidArgumentException + */ + protected function createController($controller) + { + if (false === strpos($controller, '::')) { + throw new \InvalidArgumentException(sprintf('Unable to find controller "%s".', $controller)); + } + + list($class, $method) = explode('::', $controller, 2); + + if (!class_exists($class)) { + throw new \InvalidArgumentException(sprintf('Class "%s" does not exist.', $class)); + } + + return array($this->instantiateController($class), $method); + } + + /** + * Returns an instantiated controller. + * + * @param string $class A class name + * + * @return object + */ + protected function instantiateController($class) + { + return new $class(); + } + + private function getControllerError($callable) + { + if (is_string($callable)) { + if (false !== strpos($callable, '::')) { + $callable = explode('::', $callable); + } + + if (class_exists($callable) && !method_exists($callable, '__invoke')) { + return sprintf('Class "%s" does not have a method "__invoke".', $callable); + } + + if (!function_exists($callable)) { + return sprintf('Function "%s" does not exist.', $callable); + } + } + + if (!is_array($callable)) { + return sprintf('Invalid type for controller given, expected string or array, got "%s".', gettype($callable)); + } + + if (2 !== count($callable)) { + return 'Invalid format for controller, expected array(controller, method) or controller::method.'; + } + + list($controller, $method) = $callable; + + if (is_string($controller) && !class_exists($controller)) { + return sprintf('Class "%s" does not exist.', $controller); + } + + $className = is_object($controller) ? get_class($controller) : $controller; + + if (method_exists($controller, $method)) { + return sprintf('Method "%s" on class "%s" should be public and non-abstract.', $method, $className); + } + + $collection = get_class_methods($controller); + + $alternatives = array(); + + foreach ($collection as $item) { + $lev = levenshtein($method, $item); + + if ($lev <= strlen($method) / 3 || false !== strpos($item, $method)) { + $alternatives[] = $item; + } + } + + asort($alternatives); + + $message = sprintf('Expected method "%s" on class "%s"', $method, $className); + + if (count($alternatives) > 0) { + $message .= sprintf(', did you mean "%s"?', implode('", "', $alternatives)); + } else { + $message .= sprintf('. Available methods: "%s".', implode('", "', $collection)); + } + + return $message; + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/Controller/ControllerResolverInterface.php b/lib/silex/vendor/symfony/http-kernel/Controller/ControllerResolverInterface.php new file mode 100644 index 000000000..afe9fb733 --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Controller/ControllerResolverInterface.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\HttpKernel\Controller; + +use Symfony\Component\HttpFoundation\Request; + +/** + * A ControllerResolverInterface implementation knows how to determine the + * controller to execute based on a Request object. + * + * It can also determine the arguments to pass to the Controller. + * + * A Controller can be any valid PHP callable. + * + * @author Fabien Potencier + */ +interface ControllerResolverInterface +{ + /** + * Returns the Controller instance associated with a Request. + * + * As several resolvers can exist for a single application, a resolver must + * return false when it is not able to determine the controller. + * + * The resolver must only throw an exception when it should be able to load + * controller but cannot because of some errors made by the developer. + * + * @return callable|false A PHP callable representing the Controller, + * or false if this resolver is not able to determine the controller + * + * @throws \LogicException If the controller can't be found + */ + public function getController(Request $request); + + /** + * Returns the arguments to pass to the controller. + * + * @param Request $request A Request instance + * @param callable $controller A PHP callable + * + * @return array An array of arguments to pass to the controller + * + * @throws \RuntimeException When value for argument given is not provided + * + * @deprecated This method is deprecated as of 3.1 and will be removed in 4.0. Please use the {@see ArgumentResolverInterface} instead. + */ + public function getArguments(Request $request, $controller); +} diff --git a/lib/silex/vendor/symfony/http-kernel/Controller/TraceableArgumentResolver.php b/lib/silex/vendor/symfony/http-kernel/Controller/TraceableArgumentResolver.php new file mode 100644 index 000000000..6fb0fa66a --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Controller/TraceableArgumentResolver.php @@ -0,0 +1,44 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Controller; + +use Symfony\Component\Stopwatch\Stopwatch; +use Symfony\Component\HttpFoundation\Request; + +/** + * @author Fabien Potencier + */ +class TraceableArgumentResolver implements ArgumentResolverInterface +{ + private $resolver; + private $stopwatch; + + public function __construct(ArgumentResolverInterface $resolver, Stopwatch $stopwatch) + { + $this->resolver = $resolver; + $this->stopwatch = $stopwatch; + } + + /** + * {@inheritdoc} + */ + public function getArguments(Request $request, $controller) + { + $e = $this->stopwatch->start('controller.get_arguments'); + + $ret = $this->resolver->getArguments($request, $controller); + + $e->stop(); + + return $ret; + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/Controller/TraceableControllerResolver.php b/lib/silex/vendor/symfony/http-kernel/Controller/TraceableControllerResolver.php new file mode 100644 index 000000000..750107714 --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Controller/TraceableControllerResolver.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\HttpKernel\Controller; + +use Symfony\Component\Stopwatch\Stopwatch; +use Symfony\Component\HttpFoundation\Request; + +/** + * @author Fabien Potencier + */ +class TraceableControllerResolver implements ControllerResolverInterface, ArgumentResolverInterface +{ + private $resolver; + private $stopwatch; + private $argumentResolver; + + public function __construct(ControllerResolverInterface $resolver, Stopwatch $stopwatch, ArgumentResolverInterface $argumentResolver = null) + { + $this->resolver = $resolver; + $this->stopwatch = $stopwatch; + $this->argumentResolver = $argumentResolver; + + // BC + if (null === $this->argumentResolver) { + $this->argumentResolver = $resolver; + } + + if (!$this->argumentResolver instanceof TraceableArgumentResolver) { + $this->argumentResolver = new TraceableArgumentResolver($this->argumentResolver, $this->stopwatch); + } + } + + /** + * {@inheritdoc} + */ + public function getController(Request $request) + { + $e = $this->stopwatch->start('controller.get_callable'); + + $ret = $this->resolver->getController($request); + + $e->stop(); + + return $ret; + } + + /** + * {@inheritdoc} + * + * @deprecated This method is deprecated as of 3.1 and will be removed in 4.0. + */ + public function getArguments(Request $request, $controller) + { + @trigger_error(sprintf('The %s method is deprecated as of 3.1 and will be removed in 4.0. Please use the %s instead.', __METHOD__, TraceableArgumentResolver::class), E_USER_DEPRECATED); + + $ret = $this->argumentResolver->getArguments($request, $controller); + + return $ret; + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/ControllerMetadata/ArgumentMetadata.php b/lib/silex/vendor/symfony/http-kernel/ControllerMetadata/ArgumentMetadata.php new file mode 100644 index 000000000..32316a8d5 --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/ControllerMetadata/ArgumentMetadata.php @@ -0,0 +1,115 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\ControllerMetadata; + +/** + * Responsible for storing metadata of an argument. + * + * @author Iltar van der Berg + */ +class ArgumentMetadata +{ + private $name; + private $type; + private $isVariadic; + private $hasDefaultValue; + private $defaultValue; + private $isNullable; + + /** + * @param string $name + * @param string $type + * @param bool $isVariadic + * @param bool $hasDefaultValue + * @param mixed $defaultValue + * @param bool $isNullable + */ + public function __construct($name, $type, $isVariadic, $hasDefaultValue, $defaultValue, $isNullable = false) + { + $this->name = $name; + $this->type = $type; + $this->isVariadic = $isVariadic; + $this->hasDefaultValue = $hasDefaultValue; + $this->defaultValue = $defaultValue; + $this->isNullable = $isNullable || null === $type || ($hasDefaultValue && null === $defaultValue); + } + + /** + * Returns the name as given in PHP, $foo would yield "foo". + * + * @return string + */ + public function getName() + { + return $this->name; + } + + /** + * Returns the type of the argument. + * + * The type is the PHP class in 5.5+ and additionally the basic type in PHP 7.0+. + * + * @return string + */ + public function getType() + { + return $this->type; + } + + /** + * Returns whether the argument is defined as "...$variadic". + * + * @return bool + */ + public function isVariadic() + { + return $this->isVariadic; + } + + /** + * Returns whether the argument has a default value. + * + * Implies whether an argument is optional. + * + * @return bool + */ + public function hasDefaultValue() + { + return $this->hasDefaultValue; + } + + /** + * Returns whether the argument accepts null values. + * + * @return bool + */ + public function isNullable() + { + return $this->isNullable; + } + + /** + * Returns the default value of the argument. + * + * @throws \LogicException if no default value is present; {@see self::hasDefaultValue()} + * + * @return mixed + */ + public function getDefaultValue() + { + if (!$this->hasDefaultValue) { + throw new \LogicException(sprintf('Argument $%s does not have a default value. Use %s::hasDefaultValue() to avoid this exception.', $this->name, __CLASS__)); + } + + return $this->defaultValue; + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/ControllerMetadata/ArgumentMetadataFactory.php b/lib/silex/vendor/symfony/http-kernel/ControllerMetadata/ArgumentMetadataFactory.php new file mode 100644 index 000000000..d1e7af206 --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/ControllerMetadata/ArgumentMetadataFactory.php @@ -0,0 +1,129 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\ControllerMetadata; + +/** + * Builds {@see ArgumentMetadata} objects based on the given Controller. + * + * @author Iltar van der Berg + */ +final class ArgumentMetadataFactory implements ArgumentMetadataFactoryInterface +{ + /** + * If the ...$arg functionality is available. + * + * Requires at least PHP 5.6.0 or HHVM 3.9.1 + * + * @var bool + */ + private $supportsVariadic; + + /** + * If the reflection supports the getType() method to resolve types. + * + * Requires at least PHP 7.0.0 or HHVM 3.11.0 + * + * @var bool + */ + private $supportsParameterType; + + public function __construct() + { + $this->supportsVariadic = method_exists('ReflectionParameter', 'isVariadic'); + $this->supportsParameterType = method_exists('ReflectionParameter', 'getType'); + } + + /** + * {@inheritdoc} + */ + public function createArgumentMetadata($controller) + { + $arguments = array(); + + if (is_array($controller)) { + $reflection = new \ReflectionMethod($controller[0], $controller[1]); + } elseif (is_object($controller) && !$controller instanceof \Closure) { + $reflection = (new \ReflectionObject($controller))->getMethod('__invoke'); + } else { + $reflection = new \ReflectionFunction($controller); + } + + foreach ($reflection->getParameters() as $param) { + $arguments[] = new ArgumentMetadata($param->getName(), $this->getType($param), $this->isVariadic($param), $this->hasDefaultValue($param), $this->getDefaultValue($param), $param->allowsNull()); + } + + return $arguments; + } + + /** + * Returns whether an argument is variadic. + * + * @param \ReflectionParameter $parameter + * + * @return bool + */ + private function isVariadic(\ReflectionParameter $parameter) + { + return $this->supportsVariadic && $parameter->isVariadic(); + } + + /** + * Determines whether an argument has a default value. + * + * @param \ReflectionParameter $parameter + * + * @return bool + */ + private function hasDefaultValue(\ReflectionParameter $parameter) + { + return $parameter->isDefaultValueAvailable(); + } + + /** + * Returns a default value if available. + * + * @param \ReflectionParameter $parameter + * + * @return mixed|null + */ + private function getDefaultValue(\ReflectionParameter $parameter) + { + return $this->hasDefaultValue($parameter) ? $parameter->getDefaultValue() : null; + } + + /** + * Returns an associated type to the given parameter if available. + * + * @param \ReflectionParameter $parameter + * + * @return null|string + */ + private function getType(\ReflectionParameter $parameter) + { + if ($this->supportsParameterType) { + if (!$type = $parameter->getType()) { + return; + } + $typeName = $type instanceof \ReflectionNamedType ? $type->getName() : $type->__toString(); + if ('array' === $typeName && !$type->isBuiltin()) { + // Special case for HHVM with variadics + return; + } + + return $typeName; + } + + if (preg_match('/^(?:[^ ]++ ){4}([a-zA-Z_\x7F-\xFF][^ ]++)/', $parameter, $info)) { + return $info[1]; + } + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/ControllerMetadata/ArgumentMetadataFactoryInterface.php b/lib/silex/vendor/symfony/http-kernel/ControllerMetadata/ArgumentMetadataFactoryInterface.php new file mode 100644 index 000000000..6ea179d78 --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/ControllerMetadata/ArgumentMetadataFactoryInterface.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\ControllerMetadata; + +/** + * Builds method argument data. + * + * @author Iltar van der Berg + */ +interface ArgumentMetadataFactoryInterface +{ + /** + * @param mixed $controller The controller to resolve the arguments for + * + * @return ArgumentMetadata[] + */ + public function createArgumentMetadata($controller); +} diff --git a/lib/silex/vendor/symfony/http-kernel/DataCollector/AjaxDataCollector.php b/lib/silex/vendor/symfony/http-kernel/DataCollector/AjaxDataCollector.php new file mode 100644 index 000000000..370a874fe --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/DataCollector/AjaxDataCollector.php @@ -0,0 +1,38 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\DataCollector; + +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +/** + * AjaxDataCollector. + * + * @author Bart van den Burg + */ +class AjaxDataCollector extends DataCollector +{ + public function collect(Request $request, Response $response, \Exception $exception = null) + { + // all collecting is done client side + } + + public function reset() + { + // all collecting is done client side + } + + public function getName() + { + return 'ajax'; + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/DataCollector/ConfigDataCollector.php b/lib/silex/vendor/symfony/http-kernel/DataCollector/ConfigDataCollector.php new file mode 100644 index 000000000..5675f0736 --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/DataCollector/ConfigDataCollector.php @@ -0,0 +1,332 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\DataCollector; + +use Symfony\Component\HttpKernel\KernelInterface; +use Symfony\Component\HttpKernel\Kernel; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\VarDumper\Caster\LinkStub; + +/** + * @author Fabien Potencier + */ +class ConfigDataCollector extends DataCollector implements LateDataCollectorInterface +{ + /** + * @var KernelInterface + */ + private $kernel; + private $name; + private $version; + private $hasVarDumper; + + /** + * @param string $name The name of the application using the web profiler + * @param string $version The version of the application using the web profiler + */ + public function __construct($name = null, $version = null) + { + $this->name = $name; + $this->version = $version; + $this->hasVarDumper = class_exists(LinkStub::class); + } + + /** + * Sets the Kernel associated with this Request. + */ + public function setKernel(KernelInterface $kernel = null) + { + $this->kernel = $kernel; + } + + /** + * {@inheritdoc} + */ + public function collect(Request $request, Response $response, \Exception $exception = null) + { + $this->data = array( + 'app_name' => $this->name, + 'app_version' => $this->version, + 'token' => $response->headers->get('X-Debug-Token'), + 'symfony_version' => Kernel::VERSION, + 'symfony_state' => 'unknown', + 'name' => isset($this->kernel) ? $this->kernel->getName() : 'n/a', + 'env' => isset($this->kernel) ? $this->kernel->getEnvironment() : 'n/a', + 'debug' => isset($this->kernel) ? $this->kernel->isDebug() : 'n/a', + 'php_version' => PHP_VERSION, + 'php_architecture' => PHP_INT_SIZE * 8, + 'php_intl_locale' => class_exists('Locale', false) && \Locale::getDefault() ? \Locale::getDefault() : 'n/a', + 'php_timezone' => date_default_timezone_get(), + 'xdebug_enabled' => extension_loaded('xdebug'), + 'apcu_enabled' => extension_loaded('apcu') && ini_get('apc.enabled'), + 'zend_opcache_enabled' => extension_loaded('Zend OPcache') && ini_get('opcache.enable'), + 'bundles' => array(), + 'sapi_name' => PHP_SAPI, + ); + + if (isset($this->kernel)) { + foreach ($this->kernel->getBundles() as $name => $bundle) { + $this->data['bundles'][$name] = $this->hasVarDumper ? new LinkStub($bundle->getPath()) : $bundle->getPath(); + } + + $this->data['symfony_state'] = $this->determineSymfonyState(); + $this->data['symfony_minor_version'] = sprintf('%s.%s', Kernel::MAJOR_VERSION, Kernel::MINOR_VERSION); + $eom = \DateTime::createFromFormat('m/Y', Kernel::END_OF_MAINTENANCE); + $eol = \DateTime::createFromFormat('m/Y', Kernel::END_OF_LIFE); + $this->data['symfony_eom'] = $eom->format('F Y'); + $this->data['symfony_eol'] = $eol->format('F Y'); + } + + if (preg_match('~^(\d+(?:\.\d+)*)(.+)?$~', $this->data['php_version'], $matches) && isset($matches[2])) { + $this->data['php_version'] = $matches[1]; + $this->data['php_version_extra'] = $matches[2]; + } + } + + /** + * {@inheritdoc} + */ + public function reset() + { + $this->data = array(); + } + + public function lateCollect() + { + $this->data = $this->cloneVar($this->data); + } + + public function getApplicationName() + { + return $this->data['app_name']; + } + + public function getApplicationVersion() + { + return $this->data['app_version']; + } + + /** + * Gets the token. + * + * @return string The token + */ + public function getToken() + { + return $this->data['token']; + } + + /** + * Gets the Symfony version. + * + * @return string The Symfony version + */ + public function getSymfonyVersion() + { + return $this->data['symfony_version']; + } + + /** + * Returns the state of the current Symfony release. + * + * @return string One of: unknown, dev, stable, eom, eol + */ + public function getSymfonyState() + { + return $this->data['symfony_state']; + } + + /** + * Returns the minor Symfony version used (without patch numbers of extra + * suffix like "RC", "beta", etc.). + * + * @return string + */ + public function getSymfonyMinorVersion() + { + return $this->data['symfony_minor_version']; + } + + /** + * Returns the human redable date when this Symfony version ends its + * maintenance period. + * + * @return string + */ + public function getSymfonyEom() + { + return $this->data['symfony_eom']; + } + + /** + * Returns the human redable date when this Symfony version reaches its + * "end of life" and won't receive bugs or security fixes. + * + * @return string + */ + public function getSymfonyEol() + { + return $this->data['symfony_eol']; + } + + /** + * Gets the PHP version. + * + * @return string The PHP version + */ + public function getPhpVersion() + { + return $this->data['php_version']; + } + + /** + * Gets the PHP version extra part. + * + * @return string|null The extra part + */ + public function getPhpVersionExtra() + { + return isset($this->data['php_version_extra']) ? $this->data['php_version_extra'] : null; + } + + /** + * @return int The PHP architecture as number of bits (e.g. 32 or 64) + */ + public function getPhpArchitecture() + { + return $this->data['php_architecture']; + } + + /** + * @return string + */ + public function getPhpIntlLocale() + { + return $this->data['php_intl_locale']; + } + + /** + * @return string + */ + public function getPhpTimezone() + { + return $this->data['php_timezone']; + } + + /** + * Gets the application name. + * + * @return string The application name + */ + public function getAppName() + { + return $this->data['name']; + } + + /** + * Gets the environment. + * + * @return string The environment + */ + public function getEnv() + { + return $this->data['env']; + } + + /** + * Returns true if the debug is enabled. + * + * @return bool true if debug is enabled, false otherwise + */ + public function isDebug() + { + return $this->data['debug']; + } + + /** + * Returns true if the XDebug is enabled. + * + * @return bool true if XDebug is enabled, false otherwise + */ + public function hasXDebug() + { + return $this->data['xdebug_enabled']; + } + + /** + * Returns true if APCu is enabled. + * + * @return bool true if APCu is enabled, false otherwise + */ + public function hasApcu() + { + return $this->data['apcu_enabled']; + } + + /** + * Returns true if Zend OPcache is enabled. + * + * @return bool true if Zend OPcache is enabled, false otherwise + */ + public function hasZendOpcache() + { + return $this->data['zend_opcache_enabled']; + } + + public function getBundles() + { + return $this->data['bundles']; + } + + /** + * Gets the PHP SAPI name. + * + * @return string The environment + */ + public function getSapiName() + { + return $this->data['sapi_name']; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'config'; + } + + /** + * Tries to retrieve information about the current Symfony version. + * + * @return string One of: dev, stable, eom, eol + */ + private function determineSymfonyState() + { + $now = new \DateTime(); + $eom = \DateTime::createFromFormat('m/Y', Kernel::END_OF_MAINTENANCE)->modify('last day of this month'); + $eol = \DateTime::createFromFormat('m/Y', Kernel::END_OF_LIFE)->modify('last day of this month'); + + if ($now > $eol) { + $versionState = 'eol'; + } elseif ($now > $eom) { + $versionState = 'eom'; + } elseif ('' !== Kernel::EXTRA_VERSION) { + $versionState = 'dev'; + } else { + $versionState = 'stable'; + } + + return $versionState; + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/DataCollector/DataCollector.php b/lib/silex/vendor/symfony/http-kernel/DataCollector/DataCollector.php new file mode 100644 index 000000000..30887ab91 --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/DataCollector/DataCollector.php @@ -0,0 +1,128 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\DataCollector; + +use Symfony\Component\HttpKernel\DataCollector\Util\ValueExporter; +use Symfony\Component\VarDumper\Caster\CutStub; +use Symfony\Component\VarDumper\Cloner\ClonerInterface; +use Symfony\Component\VarDumper\Cloner\Data; +use Symfony\Component\VarDumper\Cloner\Stub; +use Symfony\Component\VarDumper\Cloner\VarCloner; + +/** + * DataCollector. + * + * Children of this class must store the collected data in the data property. + * + * @author Fabien Potencier + * @author Bernhard Schussek + */ +abstract class DataCollector implements DataCollectorInterface, \Serializable +{ + protected $data = array(); + + /** + * @var ValueExporter + */ + private $valueExporter; + + /** + * @var ClonerInterface + */ + private $cloner; + + public function serialize() + { + return serialize($this->data); + } + + public function unserialize($data) + { + $this->data = unserialize($data); + } + + /** + * Converts the variable into a serializable Data instance. + * + * This array can be displayed in the template using + * the VarDumper component. + * + * @param mixed $var + * + * @return Data + */ + protected function cloneVar($var) + { + if ($var instanceof Data) { + return $var; + } + if (null === $this->cloner) { + if (class_exists(CutStub::class)) { + $this->cloner = new VarCloner(); + $this->cloner->setMaxItems(-1); + $this->cloner->addCasters($this->getCasters()); + } else { + @trigger_error(sprintf('Using the %s() method without the VarDumper component is deprecated since Symfony 3.2 and won\'t be supported in 4.0. Install symfony/var-dumper version 3.2 or above.', __METHOD__), E_USER_DEPRECATED); + $this->cloner = false; + } + } + if (false === $this->cloner) { + if (null === $this->valueExporter) { + $this->valueExporter = new ValueExporter(); + } + + return $this->valueExporter->exportValue($var); + } + + return $this->cloner->cloneVar($var); + } + + /** + * Converts a PHP variable to a string. + * + * @param mixed $var A PHP variable + * + * @return string The string representation of the variable + * + * @deprecated since version 3.2, to be removed in 4.0. Use cloneVar() instead. + */ + protected function varToString($var) + { + @trigger_error(sprintf('The %s() method is deprecated since Symfony 3.2 and will be removed in 4.0. Use cloneVar() instead.', __METHOD__), E_USER_DEPRECATED); + + if (null === $this->valueExporter) { + $this->valueExporter = new ValueExporter(); + } + + return $this->valueExporter->exportValue($var); + } + + /** + * @return callable[] The casters to add to the cloner + */ + protected function getCasters() + { + return array( + '*' => function ($v, array $a, Stub $s, $isNested) { + if (!$v instanceof Stub) { + foreach ($a as $k => $v) { + if (is_object($v) && !$v instanceof \DateTimeInterface && !$v instanceof Stub) { + $a[$k] = new CutStub($v); + } + } + } + + return $a; + }, + ); + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/DataCollector/DataCollectorInterface.php b/lib/silex/vendor/symfony/http-kernel/DataCollector/DataCollectorInterface.php new file mode 100644 index 000000000..2470089b8 --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/DataCollector/DataCollectorInterface.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\HttpKernel\DataCollector; + +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +/** + * DataCollectorInterface. + * + * @author Fabien Potencier + * + * @method reset() Resets this data collector to its initial state. + */ +interface DataCollectorInterface +{ + /** + * Collects data for the given Request and Response. + */ + public function collect(Request $request, Response $response, \Exception $exception = null); + + /** + * Returns the name of the collector. + * + * @return string The collector name + */ + public function getName(); +} diff --git a/lib/silex/vendor/symfony/http-kernel/DataCollector/DumpDataCollector.php b/lib/silex/vendor/symfony/http-kernel/DataCollector/DumpDataCollector.php new file mode 100644 index 000000000..d0ca671e0 --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/DataCollector/DumpDataCollector.php @@ -0,0 +1,315 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\DataCollector; + +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\RequestStack; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\Stopwatch\Stopwatch; +use Symfony\Component\VarDumper\Cloner\Data; +use Symfony\Component\VarDumper\Cloner\VarCloner; +use Symfony\Component\VarDumper\Dumper\CliDumper; +use Symfony\Component\VarDumper\Dumper\HtmlDumper; +use Symfony\Component\VarDumper\Dumper\DataDumperInterface; +use Twig\Template; + +/** + * @author Nicolas Grekas + */ +class DumpDataCollector extends DataCollector implements DataDumperInterface +{ + private $stopwatch; + private $fileLinkFormat; + private $dataCount = 0; + private $isCollected = true; + private $clonesCount = 0; + private $clonesIndex = 0; + private $rootRefs; + private $charset; + private $requestStack; + private $dumper; + private $dumperIsInjected; + + public function __construct(Stopwatch $stopwatch = null, $fileLinkFormat = null, $charset = null, RequestStack $requestStack = null, DataDumperInterface $dumper = null) + { + $this->stopwatch = $stopwatch; + $this->fileLinkFormat = $fileLinkFormat ?: ini_get('xdebug.file_link_format') ?: get_cfg_var('xdebug.file_link_format'); + $this->charset = $charset ?: ini_get('php.output_encoding') ?: ini_get('default_charset') ?: 'UTF-8'; + $this->requestStack = $requestStack; + $this->dumper = $dumper; + $this->dumperIsInjected = null !== $dumper; + + // All clones share these properties by reference: + $this->rootRefs = array( + &$this->data, + &$this->dataCount, + &$this->isCollected, + &$this->clonesCount, + ); + } + + public function __clone() + { + $this->clonesIndex = ++$this->clonesCount; + } + + public function dump(Data $data) + { + if ($this->stopwatch) { + $this->stopwatch->start('dump'); + } + if ($this->isCollected) { + $this->isCollected = false; + } + + $trace = debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT | DEBUG_BACKTRACE_IGNORE_ARGS, 7); + + $file = $trace[0]['file']; + $line = $trace[0]['line']; + $name = false; + $fileExcerpt = false; + + for ($i = 1; $i < 7; ++$i) { + if (isset($trace[$i]['class'], $trace[$i]['function']) + && 'dump' === $trace[$i]['function'] + && 'Symfony\Component\VarDumper\VarDumper' === $trace[$i]['class'] + ) { + $file = $trace[$i]['file']; + $line = $trace[$i]['line']; + + while (++$i < 7) { + 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 = array(); + + 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); + } + + if ($this->dumper) { + $this->doDump($data, $name, $file, $line); + } + + $this->data[] = compact('data', 'name', 'file', 'line', 'fileExcerpt'); + ++$this->dataCount; + + if ($this->stopwatch) { + $this->stopwatch->stop('dump'); + } + } + + public function collect(Request $request, Response $response, \Exception $exception = null) + { + // Sub-requests and programmatic calls stay in the collected profile. + if ($this->dumper || ($this->requestStack && $this->requestStack->getMasterRequest() !== $request) || $request->isXmlHttpRequest() || $request->headers->has('Origin')) { + return; + } + + // In all other conditions that remove the web debug toolbar, dumps are written on the output. + if (!$this->requestStack + || !$response->headers->has('X-Debug-Token') + || $response->isRedirection() + || ($response->headers->has('Content-Type') && false === strpos($response->headers->get('Content-Type'), 'html')) + || 'html' !== $request->getRequestFormat() + || false === strripos($response->getContent(), '') + ) { + if ($response->headers->has('Content-Type') && false !== strpos($response->headers->get('Content-Type'), 'html')) { + $this->dumper = new HtmlDumper('php://output', $this->charset); + $this->dumper->setDisplayOptions(array('fileLinkFormat' => $this->fileLinkFormat)); + } else { + $this->dumper = new CliDumper('php://output', $this->charset); + } + + foreach ($this->data as $dump) { + $this->doDump($dump['data'], $dump['name'], $dump['file'], $dump['line']); + } + } + } + + public function reset() + { + if ($this->stopwatch) { + $this->stopwatch->reset(); + } + $this->data = array(); + $this->dataCount = 0; + $this->isCollected = false; + $this->clonesCount = 0; + $this->clonesIndex = 0; + } + + public function serialize() + { + if ($this->clonesCount !== $this->clonesIndex) { + return 'a:0:{}'; + } + + $this->data[] = $this->fileLinkFormat; + $this->data[] = $this->charset; + $ser = serialize($this->data); + $this->data = array(); + $this->dataCount = 0; + $this->isCollected = true; + if (!$this->dumperIsInjected) { + $this->dumper = null; + } + + return $ser; + } + + public function unserialize($data) + { + parent::unserialize($data); + $charset = array_pop($this->data); + $fileLinkFormat = array_pop($this->data); + $this->dataCount = count($this->data); + self::__construct($this->stopwatch, $fileLinkFormat, $charset); + } + + public function getDumpsCount() + { + return $this->dataCount; + } + + public function getDumps($format, $maxDepthLimit = -1, $maxItemsPerDepth = -1) + { + $data = fopen('php://memory', 'r+b'); + + if ('html' === $format) { + $dumper = new HtmlDumper($data, $this->charset); + $dumper->setDisplayOptions(array('fileLinkFormat' => $this->fileLinkFormat)); + } else { + throw new \InvalidArgumentException(sprintf('Invalid dump format: %s', $format)); + } + $dumps = array(); + + foreach ($this->data as $dump) { + $dumper->dump($dump['data']->withMaxDepth($maxDepthLimit)->withMaxItemsPerDepth($maxItemsPerDepth)); + $dump['data'] = stream_get_contents($data, -1, 0); + ftruncate($data, 0); + rewind($data); + $dumps[] = $dump; + } + + return $dumps; + } + + public function getName() + { + return 'dump'; + } + + public function __destruct() + { + if (0 === $this->clonesCount-- && !$this->isCollected && $this->data) { + $this->clonesCount = 0; + $this->isCollected = true; + + $h = headers_list(); + $i = count($h); + array_unshift($h, 'Content-Type: '.ini_get('default_mimetype')); + while (0 !== stripos($h[$i], 'Content-Type:')) { + --$i; + } + + if ('cli' !== PHP_SAPI && stripos($h[$i], 'html')) { + $this->dumper = new HtmlDumper('php://output', $this->charset); + $this->dumper->setDisplayOptions(array('fileLinkFormat' => $this->fileLinkFormat)); + } else { + $this->dumper = new CliDumper('php://output', $this->charset); + } + + foreach ($this->data as $i => $dump) { + $this->data[$i] = null; + $this->doDump($dump['data'], $dump['name'], $dump['file'], $dump['line']); + } + + $this->data = array(); + $this->dataCount = 0; + } + } + + private function doDump($data, $name, $file, $line) + { + if ($this->dumper instanceof CliDumper) { + $contextDumper = function ($name, $file, $line, $fmt) { + if ($this instanceof HtmlDumper) { + if ($file) { + $s = $this->style('meta', '%s'); + $f = strip_tags($this->style('', $file)); + $name = strip_tags($this->style('', $name)); + if ($fmt && $link = is_string($fmt) ? strtr($fmt, array('%f' => $file, '%l' => $line)) : $fmt->format($file, $line)) { + $name = sprintf(''.$s.'', strip_tags($this->style('', $link)), $f, $name); + } else { + $name = sprintf(''.$s.'', $f, $name); + } + } else { + $name = $this->style('meta', $name); + } + $this->line = $name.' on line '.$this->style('meta', $line).':'; + } else { + $this->line = $this->style('meta', $name).' on line '.$this->style('meta', $line).':'; + } + $this->dumpLine(0); + }; + $contextDumper = $contextDumper->bindTo($this->dumper, $this->dumper); + $contextDumper($name, $file, $line, $this->fileLinkFormat); + } else { + $cloner = new VarCloner(); + $this->dumper->dump($cloner->cloneVar($name.' on line '.$line.':')); + } + $this->dumper->dump($data); + } + + private function htmlEncode($s) + { + $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/lib/silex/vendor/symfony/http-kernel/DataCollector/EventDataCollector.php b/lib/silex/vendor/symfony/http-kernel/DataCollector/EventDataCollector.php new file mode 100644 index 000000000..0a095ab68 --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/DataCollector/EventDataCollector.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\HttpKernel\DataCollector; + +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; +use Symfony\Component\EventDispatcher\Debug\TraceableEventDispatcherInterface; + +/** + * EventDataCollector. + * + * @author Fabien Potencier + */ +class EventDataCollector extends DataCollector implements LateDataCollectorInterface +{ + protected $dispatcher; + + public function __construct(EventDispatcherInterface $dispatcher = null) + { + if ($dispatcher instanceof TraceableEventDispatcherInterface && !method_exists($dispatcher, 'reset')) { + @trigger_error(sprintf('Implementing "%s" without the "reset()" method is deprecated since Symfony 3.4 and will be unsupported in 4.0 for class "%s".', TraceableEventDispatcherInterface::class, \get_class($dispatcher)), E_USER_DEPRECATED); + } + $this->dispatcher = $dispatcher; + } + + /** + * {@inheritdoc} + */ + public function collect(Request $request, Response $response, \Exception $exception = null) + { + $this->data = array( + 'called_listeners' => array(), + 'not_called_listeners' => array(), + ); + } + + public function reset() + { + $this->data = array(); + + if ($this->dispatcher instanceof TraceableEventDispatcherInterface) { + if (!method_exists($this->dispatcher, 'reset')) { + return; // @deprecated + } + + $this->dispatcher->reset(); + } + } + + public function lateCollect() + { + if ($this->dispatcher instanceof TraceableEventDispatcherInterface) { + $this->setCalledListeners($this->dispatcher->getCalledListeners()); + $this->setNotCalledListeners($this->dispatcher->getNotCalledListeners()); + } + $this->data = $this->cloneVar($this->data); + } + + /** + * Sets the called listeners. + * + * @param array $listeners An array of called listeners + * + * @see TraceableEventDispatcherInterface + */ + public function setCalledListeners(array $listeners) + { + $this->data['called_listeners'] = $listeners; + } + + /** + * Gets the called listeners. + * + * @return array An array of called listeners + * + * @see TraceableEventDispatcherInterface + */ + public function getCalledListeners() + { + return $this->data['called_listeners']; + } + + /** + * Sets the not called listeners. + * + * @param array $listeners An array of not called listeners + * + * @see TraceableEventDispatcherInterface + */ + public function setNotCalledListeners(array $listeners) + { + $this->data['not_called_listeners'] = $listeners; + } + + /** + * Gets the not called listeners. + * + * @return array An array of not called listeners + * + * @see TraceableEventDispatcherInterface + */ + public function getNotCalledListeners() + { + return $this->data['not_called_listeners']; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'events'; + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/DataCollector/ExceptionDataCollector.php b/lib/silex/vendor/symfony/http-kernel/DataCollector/ExceptionDataCollector.php new file mode 100644 index 000000000..7a25f1492 --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/DataCollector/ExceptionDataCollector.php @@ -0,0 +1,112 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\DataCollector; + +use Symfony\Component\Debug\Exception\FlattenException; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +/** + * ExceptionDataCollector. + * + * @author Fabien Potencier + */ +class ExceptionDataCollector extends DataCollector +{ + /** + * {@inheritdoc} + */ + public function collect(Request $request, Response $response, \Exception $exception = null) + { + if (null !== $exception) { + $this->data = array( + 'exception' => FlattenException::create($exception), + ); + } + } + + /** + * {@inheritdoc} + */ + public function reset() + { + $this->data = array(); + } + + /** + * Checks if the exception is not null. + * + * @return bool true if the exception is not null, false otherwise + */ + public function hasException() + { + return isset($this->data['exception']); + } + + /** + * Gets the exception. + * + * @return \Exception The exception + */ + public function getException() + { + return $this->data['exception']; + } + + /** + * Gets the exception message. + * + * @return string The exception message + */ + public function getMessage() + { + return $this->data['exception']->getMessage(); + } + + /** + * Gets the exception code. + * + * @return int The exception code + */ + public function getCode() + { + return $this->data['exception']->getCode(); + } + + /** + * Gets the status code. + * + * @return int The status code + */ + public function getStatusCode() + { + return $this->data['exception']->getStatusCode(); + } + + /** + * Gets the exception trace. + * + * @return array The exception trace + */ + public function getTrace() + { + return $this->data['exception']->getTrace(); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'exception'; + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/DataCollector/LateDataCollectorInterface.php b/lib/silex/vendor/symfony/http-kernel/DataCollector/LateDataCollectorInterface.php new file mode 100644 index 000000000..012332de4 --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/DataCollector/LateDataCollectorInterface.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\HttpKernel\DataCollector; + +/** + * LateDataCollectorInterface. + * + * @author Fabien Potencier + */ +interface LateDataCollectorInterface +{ + /** + * Collects data as late as possible. + */ + public function lateCollect(); +} diff --git a/lib/silex/vendor/symfony/http-kernel/DataCollector/LoggerDataCollector.php b/lib/silex/vendor/symfony/http-kernel/DataCollector/LoggerDataCollector.php new file mode 100644 index 000000000..03bfa5d84 --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/DataCollector/LoggerDataCollector.php @@ -0,0 +1,278 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\DataCollector; + +use Symfony\Component\Debug\Exception\SilencedErrorContext; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\Log\DebugLoggerInterface; + +/** + * LogDataCollector. + * + * @author Fabien Potencier + */ +class LoggerDataCollector extends DataCollector implements LateDataCollectorInterface +{ + private $logger; + private $containerPathPrefix; + + public function __construct($logger = null, $containerPathPrefix = null) + { + if (null !== $logger && $logger instanceof DebugLoggerInterface) { + if (!method_exists($logger, 'clear')) { + @trigger_error(sprintf('Implementing "%s" without the "clear()" method is deprecated since Symfony 3.4 and will be unsupported in 4.0 for class "%s".', DebugLoggerInterface::class, \get_class($logger)), E_USER_DEPRECATED); + } + + $this->logger = $logger; + } + + $this->containerPathPrefix = $containerPathPrefix; + } + + /** + * {@inheritdoc} + */ + public function collect(Request $request, Response $response, \Exception $exception = null) + { + // everything is done as late as possible + } + + /** + * {@inheritdoc} + */ + public function reset() + { + if ($this->logger && method_exists($this->logger, 'clear')) { + $this->logger->clear(); + } + $this->data = array(); + } + + /** + * {@inheritdoc} + */ + public function lateCollect() + { + if (null !== $this->logger) { + $containerDeprecationLogs = $this->getContainerDeprecationLogs(); + $this->data = $this->computeErrorsCount($containerDeprecationLogs); + $this->data['compiler_logs'] = $this->getContainerCompilerLogs(); + $this->data['logs'] = $this->sanitizeLogs(array_merge($this->logger->getLogs(), $containerDeprecationLogs)); + $this->data = $this->cloneVar($this->data); + } + } + + /** + * Gets the logs. + * + * @return array An array of logs + */ + public function getLogs() + { + return isset($this->data['logs']) ? $this->data['logs'] : array(); + } + + public function getPriorities() + { + return isset($this->data['priorities']) ? $this->data['priorities'] : array(); + } + + public function countErrors() + { + return isset($this->data['error_count']) ? $this->data['error_count'] : 0; + } + + public function countDeprecations() + { + return isset($this->data['deprecation_count']) ? $this->data['deprecation_count'] : 0; + } + + public function countWarnings() + { + return isset($this->data['warning_count']) ? $this->data['warning_count'] : 0; + } + + public function countScreams() + { + return isset($this->data['scream_count']) ? $this->data['scream_count'] : 0; + } + + public function getCompilerLogs() + { + return isset($this->data['compiler_logs']) ? $this->data['compiler_logs'] : array(); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'logger'; + } + + private function getContainerDeprecationLogs() + { + if (null === $this->containerPathPrefix || !file_exists($file = $this->containerPathPrefix.'Deprecations.log')) { + return array(); + } + + $bootTime = filemtime($file); + $logs = array(); + foreach (unserialize(file_get_contents($file)) as $log) { + $log['context'] = array('exception' => new SilencedErrorContext($log['type'], $log['file'], $log['line'], $log['trace'], $log['count'])); + $log['timestamp'] = $bootTime; + $log['priority'] = 100; + $log['priorityName'] = 'DEBUG'; + $log['channel'] = '-'; + $log['scream'] = false; + unset($log['type'], $log['file'], $log['line'], $log['trace'], $log['trace'], $log['count']); + $logs[] = $log; + } + + return $logs; + } + + private function getContainerCompilerLogs() + { + if (null === $this->containerPathPrefix || !file_exists($file = $this->containerPathPrefix.'Compiler.log')) { + return array(); + } + + $logs = array(); + foreach (file($file, FILE_IGNORE_NEW_LINES) as $log) { + $log = explode(': ', $log, 2); + if (!isset($log[1]) || !preg_match('/^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*+(?:\\\\[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*+)++$/', $log[0])) { + $log = array('Unknown Compiler Pass', implode(': ', $log)); + } + + $logs[$log[0]][] = array('message' => $log[1]); + } + + return $logs; + } + + private function sanitizeLogs($logs) + { + $sanitizedLogs = array(); + $silencedLogs = array(); + + foreach ($logs as $log) { + if (!$this->isSilencedOrDeprecationErrorLog($log)) { + $sanitizedLogs[] = $log; + + continue; + } + + $message = $log['message']; + $exception = $log['context']['exception']; + + if ($exception instanceof SilencedErrorContext) { + if (isset($silencedLogs[$h = spl_object_hash($exception)])) { + continue; + } + $silencedLogs[$h] = true; + + if (!isset($sanitizedLogs[$message])) { + $sanitizedLogs[$message] = $log + array( + 'errorCount' => 0, + 'scream' => true, + ); + } + $sanitizedLogs[$message]['errorCount'] += $exception->count; + + continue; + } + + $errorId = md5("{$exception->getSeverity()}/{$exception->getLine()}/{$exception->getFile()}\0{$message}", true); + + if (isset($sanitizedLogs[$errorId])) { + ++$sanitizedLogs[$errorId]['errorCount']; + } else { + $log += array( + 'errorCount' => 1, + 'scream' => false, + ); + + $sanitizedLogs[$errorId] = $log; + } + } + + return array_values($sanitizedLogs); + } + + private function isSilencedOrDeprecationErrorLog(array $log) + { + if (!isset($log['context']['exception'])) { + return false; + } + + $exception = $log['context']['exception']; + + if ($exception instanceof SilencedErrorContext) { + return true; + } + + if ($exception instanceof \ErrorException && in_array($exception->getSeverity(), array(E_DEPRECATED, E_USER_DEPRECATED), true)) { + return true; + } + + return false; + } + + private function computeErrorsCount(array $containerDeprecationLogs) + { + $silencedLogs = array(); + $count = array( + 'error_count' => $this->logger->countErrors(), + 'deprecation_count' => 0, + 'warning_count' => 0, + 'scream_count' => 0, + 'priorities' => array(), + ); + + foreach ($this->logger->getLogs() as $log) { + if (isset($count['priorities'][$log['priority']])) { + ++$count['priorities'][$log['priority']]['count']; + } else { + $count['priorities'][$log['priority']] = array( + 'count' => 1, + 'name' => $log['priorityName'], + ); + } + if ('WARNING' === $log['priorityName']) { + ++$count['warning_count']; + } + + if ($this->isSilencedOrDeprecationErrorLog($log)) { + $exception = $log['context']['exception']; + if ($exception instanceof SilencedErrorContext) { + if (isset($silencedLogs[$h = spl_object_hash($exception)])) { + continue; + } + $silencedLogs[$h] = true; + $count['scream_count'] += $exception->count; + } else { + ++$count['deprecation_count']; + } + } + } + + foreach ($containerDeprecationLogs as $deprecationLog) { + $count['deprecation_count'] += $deprecationLog['context']['exception']->count; + } + + ksort($count['priorities']); + + return $count; + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/DataCollector/MemoryDataCollector.php b/lib/silex/vendor/symfony/http-kernel/DataCollector/MemoryDataCollector.php new file mode 100644 index 000000000..8d8cc1a04 --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/DataCollector/MemoryDataCollector.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\HttpKernel\DataCollector; + +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +/** + * MemoryDataCollector. + * + * @author Fabien Potencier + */ +class MemoryDataCollector extends DataCollector implements LateDataCollectorInterface +{ + public function __construct() + { + $this->reset(); + } + + /** + * {@inheritdoc} + */ + public function collect(Request $request, Response $response, \Exception $exception = null) + { + $this->updateMemoryUsage(); + } + + /** + * {@inheritdoc} + */ + public function reset() + { + $this->data = array( + 'memory' => 0, + 'memory_limit' => $this->convertToBytes(ini_get('memory_limit')), + ); + } + + /** + * {@inheritdoc} + */ + public function lateCollect() + { + $this->updateMemoryUsage(); + } + + /** + * Gets the memory. + * + * @return int The memory + */ + public function getMemory() + { + return $this->data['memory']; + } + + /** + * Gets the PHP memory limit. + * + * @return int The memory limit + */ + public function getMemoryLimit() + { + return $this->data['memory_limit']; + } + + /** + * Updates the memory usage data. + */ + public function updateMemoryUsage() + { + $this->data['memory'] = memory_get_peak_usage(true); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'memory'; + } + + private function convertToBytes($memoryLimit) + { + if ('-1' === $memoryLimit) { + return -1; + } + + $memoryLimit = strtolower($memoryLimit); + $max = strtolower(ltrim($memoryLimit, '+')); + if (0 === strpos($max, '0x')) { + $max = intval($max, 16); + } elseif (0 === strpos($max, '0')) { + $max = intval($max, 8); + } else { + $max = (int) $max; + } + + switch (substr($memoryLimit, -1)) { + case 't': $max *= 1024; + // no break + case 'g': $max *= 1024; + // no break + case 'm': $max *= 1024; + // no break + case 'k': $max *= 1024; + } + + return $max; + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/DataCollector/RequestDataCollector.php b/lib/silex/vendor/symfony/http-kernel/DataCollector/RequestDataCollector.php new file mode 100644 index 000000000..54d17d28c --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/DataCollector/RequestDataCollector.php @@ -0,0 +1,404 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\DataCollector; + +use Symfony\Component\HttpFoundation\Cookie; +use Symfony\Component\HttpFoundation\ParameterBag; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\Event\FilterResponseEvent; +use Symfony\Component\HttpKernel\KernelEvents; +use Symfony\Component\HttpKernel\Event\FilterControllerEvent; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; + +/** + * @author Fabien Potencier + */ +class RequestDataCollector extends DataCollector implements EventSubscriberInterface, LateDataCollectorInterface +{ + protected $controllers; + + public function __construct() + { + $this->controllers = new \SplObjectStorage(); + } + + /** + * {@inheritdoc} + */ + public function collect(Request $request, Response $response, \Exception $exception = null) + { + // attributes are serialized and as they can be anything, they need to be converted to strings. + $attributes = array(); + $route = ''; + foreach ($request->attributes->all() as $key => $value) { + if ('_route' === $key) { + $route = is_object($value) ? $value->getPath() : $value; + $attributes[$key] = $route; + } else { + $attributes[$key] = $value; + } + } + + $content = null; + try { + $content = $request->getContent(); + } catch (\LogicException $e) { + // the user already got the request content as a resource + $content = false; + } + + $sessionMetadata = array(); + $sessionAttributes = array(); + $session = null; + $flashes = array(); + if ($request->hasSession()) { + $session = $request->getSession(); + if ($session->isStarted()) { + $sessionMetadata['Created'] = date(DATE_RFC822, $session->getMetadataBag()->getCreated()); + $sessionMetadata['Last used'] = date(DATE_RFC822, $session->getMetadataBag()->getLastUsed()); + $sessionMetadata['Lifetime'] = $session->getMetadataBag()->getLifetime(); + $sessionAttributes = $session->all(); + $flashes = $session->getFlashBag()->peekAll(); + } + } + + $statusCode = $response->getStatusCode(); + + $responseCookies = array(); + foreach ($response->headers->getCookies() as $cookie) { + $responseCookies[$cookie->getName()] = $cookie; + } + + $this->data = array( + 'method' => $request->getMethod(), + 'format' => $request->getRequestFormat(), + 'content' => $content, + 'content_type' => $response->headers->get('Content-Type', 'text/html'), + 'status_text' => isset(Response::$statusTexts[$statusCode]) ? Response::$statusTexts[$statusCode] : '', + 'status_code' => $statusCode, + 'request_query' => $request->query->all(), + 'request_request' => $request->request->all(), + 'request_headers' => $request->headers->all(), + 'request_server' => $request->server->all(), + 'request_cookies' => $request->cookies->all(), + 'request_attributes' => $attributes, + 'route' => $route, + 'response_headers' => $response->headers->all(), + 'response_cookies' => $responseCookies, + 'session_metadata' => $sessionMetadata, + 'session_attributes' => $sessionAttributes, + 'flashes' => $flashes, + 'path_info' => $request->getPathInfo(), + 'controller' => 'n/a', + 'locale' => $request->getLocale(), + ); + + if (isset($this->data['request_headers']['php-auth-pw'])) { + $this->data['request_headers']['php-auth-pw'] = '******'; + } + + if (isset($this->data['request_server']['PHP_AUTH_PW'])) { + $this->data['request_server']['PHP_AUTH_PW'] = '******'; + } + + if (isset($this->data['request_request']['_password'])) { + $this->data['request_request']['_password'] = '******'; + } + + foreach ($this->data as $key => $value) { + if (!is_array($value)) { + continue; + } + if ('request_headers' === $key || 'response_headers' === $key) { + $this->data[$key] = array_map(function ($v) { return isset($v[0]) && !isset($v[1]) ? $v[0] : $v; }, $value); + } + } + + if (isset($this->controllers[$request])) { + $this->data['controller'] = $this->parseController($this->controllers[$request]); + unset($this->controllers[$request]); + } + + if ($request->attributes->has('_redirected') && $redirectCookie = $request->cookies->get('sf_redirect')) { + $this->data['redirect'] = json_decode($redirectCookie, true); + + $response->headers->clearCookie('sf_redirect'); + } + + if ($response->isRedirect()) { + $response->headers->setCookie(new Cookie( + 'sf_redirect', + json_encode(array( + 'token' => $response->headers->get('x-debug-token'), + 'route' => $request->attributes->get('_route', 'n/a'), + 'method' => $request->getMethod(), + 'controller' => $this->parseController($request->attributes->get('_controller')), + 'status_code' => $statusCode, + 'status_text' => Response::$statusTexts[(int) $statusCode], + )) + )); + } + + $this->data['identifier'] = $this->data['route'] ?: (is_array($this->data['controller']) ? $this->data['controller']['class'].'::'.$this->data['controller']['method'].'()' : $this->data['controller']); + } + + public function lateCollect() + { + $this->data = $this->cloneVar($this->data); + } + + public function reset() + { + $this->data = array(); + $this->controllers = new \SplObjectStorage(); + } + + public function getMethod() + { + return $this->data['method']; + } + + public function getPathInfo() + { + return $this->data['path_info']; + } + + public function getRequestRequest() + { + return new ParameterBag($this->data['request_request']->getValue()); + } + + public function getRequestQuery() + { + return new ParameterBag($this->data['request_query']->getValue()); + } + + public function getRequestHeaders() + { + return new ParameterBag($this->data['request_headers']->getValue()); + } + + public function getRequestServer($raw = false) + { + return new ParameterBag($this->data['request_server']->getValue($raw)); + } + + public function getRequestCookies($raw = false) + { + return new ParameterBag($this->data['request_cookies']->getValue($raw)); + } + + public function getRequestAttributes() + { + return new ParameterBag($this->data['request_attributes']->getValue()); + } + + public function getResponseHeaders() + { + return new ParameterBag($this->data['response_headers']->getValue()); + } + + public function getResponseCookies() + { + return new ParameterBag($this->data['response_cookies']->getValue()); + } + + public function getSessionMetadata() + { + return $this->data['session_metadata']->getValue(); + } + + public function getSessionAttributes() + { + return $this->data['session_attributes']->getValue(); + } + + public function getFlashes() + { + return $this->data['flashes']->getValue(); + } + + public function getContent() + { + return $this->data['content']; + } + + public function getContentType() + { + return $this->data['content_type']; + } + + public function getStatusText() + { + return $this->data['status_text']; + } + + public function getStatusCode() + { + return $this->data['status_code']; + } + + public function getFormat() + { + return $this->data['format']; + } + + public function getLocale() + { + return $this->data['locale']; + } + + /** + * Gets the route name. + * + * The _route request attributes is automatically set by the Router Matcher. + * + * @return string The route + */ + public function getRoute() + { + return $this->data['route']; + } + + public function getIdentifier() + { + return $this->data['identifier']; + } + + /** + * Gets the route parameters. + * + * The _route_params request attributes is automatically set by the RouterListener. + * + * @return array The parameters + */ + public function getRouteParams() + { + return isset($this->data['request_attributes']['_route_params']) ? $this->data['request_attributes']['_route_params']->getValue() : array(); + } + + /** + * Gets the parsed controller. + * + * @return array|string The controller as a string or array of data + * with keys 'class', 'method', 'file' and 'line' + */ + public function getController() + { + return $this->data['controller']; + } + + /** + * Gets the previous request attributes. + * + * @return array|bool A legacy array of data from the previous redirection response + * or false otherwise + */ + public function getRedirect() + { + return isset($this->data['redirect']) ? $this->data['redirect'] : false; + } + + public function onKernelController(FilterControllerEvent $event) + { + $this->controllers[$event->getRequest()] = $event->getController(); + } + + public function onKernelResponse(FilterResponseEvent $event) + { + if (!$event->isMasterRequest()) { + return; + } + + if ($event->getRequest()->cookies->has('sf_redirect')) { + $event->getRequest()->attributes->set('_redirected', true); + } + } + + public static function getSubscribedEvents() + { + return array( + KernelEvents::CONTROLLER => 'onKernelController', + KernelEvents::RESPONSE => 'onKernelResponse', + ); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'request'; + } + + /** + * Parse a controller. + * + * @param mixed $controller The controller to parse + * + * @return array|string An array of controller data or a simple string + */ + protected function parseController($controller) + { + if (is_string($controller) && false !== strpos($controller, '::')) { + $controller = explode('::', $controller); + } + + if (is_array($controller)) { + try { + $r = new \ReflectionMethod($controller[0], $controller[1]); + + return array( + 'class' => is_object($controller[0]) ? get_class($controller[0]) : $controller[0], + 'method' => $controller[1], + 'file' => $r->getFileName(), + 'line' => $r->getStartLine(), + ); + } catch (\ReflectionException $e) { + if (is_callable($controller)) { + // using __call or __callStatic + return array( + 'class' => is_object($controller[0]) ? get_class($controller[0]) : $controller[0], + 'method' => $controller[1], + 'file' => 'n/a', + 'line' => 'n/a', + ); + } + } + } + + if ($controller instanceof \Closure) { + $r = new \ReflectionFunction($controller); + + return array( + 'class' => $r->getName(), + 'method' => null, + 'file' => $r->getFileName(), + 'line' => $r->getStartLine(), + ); + } + + if (is_object($controller)) { + $r = new \ReflectionClass($controller); + + return array( + 'class' => $r->getName(), + 'method' => null, + 'file' => $r->getFileName(), + 'line' => $r->getStartLine(), + ); + } + + return is_string($controller) ? $controller : 'n/a'; + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/DataCollector/RouterDataCollector.php b/lib/silex/vendor/symfony/http-kernel/DataCollector/RouterDataCollector.php new file mode 100644 index 000000000..afd34f8af --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/DataCollector/RouterDataCollector.php @@ -0,0 +1,108 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\DataCollector; + +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpFoundation\RedirectResponse; +use Symfony\Component\HttpKernel\Event\FilterControllerEvent; + +/** + * RouterDataCollector. + * + * @author Fabien Potencier + */ +class RouterDataCollector extends DataCollector +{ + /** + * @var \SplObjectStorage + */ + protected $controllers; + + public function __construct() + { + $this->reset(); + } + + /** + * {@inheritdoc} + */ + public function collect(Request $request, Response $response, \Exception $exception = null) + { + if ($response instanceof RedirectResponse) { + $this->data['redirect'] = true; + $this->data['url'] = $response->getTargetUrl(); + + if ($this->controllers->contains($request)) { + $this->data['route'] = $this->guessRoute($request, $this->controllers[$request]); + } + } + + unset($this->controllers[$request]); + } + + public function reset() + { + $this->controllers = new \SplObjectStorage(); + + $this->data = array( + 'redirect' => false, + 'url' => null, + 'route' => null, + ); + } + + protected function guessRoute(Request $request, $controller) + { + return 'n/a'; + } + + /** + * Remembers the controller associated to each request. + */ + public function onKernelController(FilterControllerEvent $event) + { + $this->controllers[$event->getRequest()] = $event->getController(); + } + + /** + * @return bool Whether this request will result in a redirect + */ + public function getRedirect() + { + return $this->data['redirect']; + } + + /** + * @return string|null The target URL + */ + public function getTargetUrl() + { + return $this->data['url']; + } + + /** + * @return string|null The target route + */ + public function getTargetRoute() + { + return $this->data['route']; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'router'; + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/DataCollector/TimeDataCollector.php b/lib/silex/vendor/symfony/http-kernel/DataCollector/TimeDataCollector.php new file mode 100644 index 000000000..e489d7759 --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/DataCollector/TimeDataCollector.php @@ -0,0 +1,149 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\DataCollector; + +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\KernelInterface; +use Symfony\Component\Stopwatch\Stopwatch; + +/** + * TimeDataCollector. + * + * @author Fabien Potencier + */ +class TimeDataCollector extends DataCollector implements LateDataCollectorInterface +{ + protected $kernel; + protected $stopwatch; + + public function __construct(KernelInterface $kernel = null, Stopwatch $stopwatch = null) + { + $this->kernel = $kernel; + $this->stopwatch = $stopwatch; + } + + /** + * {@inheritdoc} + */ + public function collect(Request $request, Response $response, \Exception $exception = null) + { + if (null !== $this->kernel) { + $startTime = $this->kernel->getStartTime(); + } else { + $startTime = $request->server->get('REQUEST_TIME_FLOAT'); + } + + $this->data = array( + 'token' => $response->headers->get('X-Debug-Token'), + 'start_time' => $startTime * 1000, + 'events' => array(), + ); + } + + /** + * {@inheritdoc} + */ + public function reset() + { + $this->data = array(); + + if (null !== $this->stopwatch) { + $this->stopwatch->reset(); + } + } + + /** + * {@inheritdoc} + */ + public function lateCollect() + { + if (null !== $this->stopwatch && isset($this->data['token'])) { + $this->setEvents($this->stopwatch->getSectionEvents($this->data['token'])); + } + unset($this->data['token']); + } + + /** + * Sets the request events. + * + * @param array $events The request events + */ + public function setEvents(array $events) + { + foreach ($events as $event) { + $event->ensureStopped(); + } + + $this->data['events'] = $events; + } + + /** + * Gets the request events. + * + * @return array The request events + */ + public function getEvents() + { + return $this->data['events']; + } + + /** + * Gets the request elapsed time. + * + * @return float The elapsed time + */ + public function getDuration() + { + if (!isset($this->data['events']['__section__'])) { + return 0; + } + + $lastEvent = $this->data['events']['__section__']; + + return $lastEvent->getOrigin() + $lastEvent->getDuration() - $this->getStartTime(); + } + + /** + * Gets the initialization time. + * + * This is the time spent until the beginning of the request handling. + * + * @return float The elapsed time + */ + public function getInitTime() + { + if (!isset($this->data['events']['__section__'])) { + return 0; + } + + return $this->data['events']['__section__']->getOrigin() - $this->getStartTime(); + } + + /** + * Gets the request time. + * + * @return int The time + */ + public function getStartTime() + { + return $this->data['start_time']; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'time'; + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/DataCollector/Util/ValueExporter.php b/lib/silex/vendor/symfony/http-kernel/DataCollector/Util/ValueExporter.php new file mode 100644 index 000000000..a71c00db9 --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/DataCollector/Util/ValueExporter.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\HttpKernel\DataCollector\Util; + +@trigger_error('The '.__NAMESPACE__.'\ValueExporter class is deprecated since Symfony 3.2 and will be removed in 4.0. Use the VarDumper component instead.', E_USER_DEPRECATED); + +/** + * @author Bernhard Schussek + * + * @deprecated since version 3.2, to be removed in 4.0. Use the VarDumper component instead. + */ +class ValueExporter +{ + /** + * Converts a PHP value to a string. + * + * @param mixed $value The PHP value + * @param int $depth Only for internal usage + * @param bool $deep Only for internal usage + * + * @return string The string representation of the given value + */ + public function exportValue($value, $depth = 1, $deep = false) + { + if ($value instanceof \__PHP_Incomplete_Class) { + return sprintf('__PHP_Incomplete_Class(%s)', $this->getClassNameFromIncomplete($value)); + } + + if (is_object($value)) { + if ($value instanceof \DateTimeInterface) { + return sprintf('Object(%s) - %s', get_class($value), $value->format(\DateTime::ATOM)); + } + + return sprintf('Object(%s)', get_class($value)); + } + + if (is_array($value)) { + if (empty($value)) { + return '[]'; + } + + $indent = str_repeat(' ', $depth); + + $a = array(); + foreach ($value as $k => $v) { + if (is_array($v)) { + $deep = true; + } + $a[] = sprintf('%s => %s', $k, $this->exportValue($v, $depth + 1, $deep)); + } + + if ($deep) { + return sprintf("[\n%s%s\n%s]", $indent, implode(sprintf(", \n%s", $indent), $a), str_repeat(' ', $depth - 1)); + } + + $s = sprintf('[%s]', implode(', ', $a)); + + if (80 > strlen($s)) { + return $s; + } + + return sprintf("[\n%s%s\n]", $indent, implode(sprintf(",\n%s", $indent), $a)); + } + + if (is_resource($value)) { + return sprintf('Resource(%s#%d)', get_resource_type($value), $value); + } + + if (null === $value) { + return 'null'; + } + + if (false === $value) { + return 'false'; + } + + if (true === $value) { + return 'true'; + } + + return (string) $value; + } + + private function getClassNameFromIncomplete(\__PHP_Incomplete_Class $value) + { + $array = new \ArrayObject($value); + + return $array['__PHP_Incomplete_Class_Name']; + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/Debug/FileLinkFormatter.php b/lib/silex/vendor/symfony/http-kernel/Debug/FileLinkFormatter.php new file mode 100644 index 000000000..c340d9b67 --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Debug/FileLinkFormatter.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\HttpKernel\Debug; + +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\RequestStack; + +/** + * Formats debug file links. + * + * @author Jérémy Romey + */ +class FileLinkFormatter implements \Serializable +{ + private $fileLinkFormat; + private $requestStack; + private $baseDir; + private $urlFormat; + + public function __construct($fileLinkFormat = null, RequestStack $requestStack = null, $baseDir = null, $urlFormat = null) + { + $fileLinkFormat = $fileLinkFormat ?: ini_get('xdebug.file_link_format') ?: get_cfg_var('xdebug.file_link_format'); + if ($fileLinkFormat && !is_array($fileLinkFormat)) { + $i = strpos($f = $fileLinkFormat, '&', max(strrpos($f, '%f'), strrpos($f, '%l'))) ?: strlen($f); + $fileLinkFormat = array(substr($f, 0, $i)) + preg_split('/&([^>]++)>/', substr($f, $i), -1, PREG_SPLIT_DELIM_CAPTURE); + } + + $this->fileLinkFormat = $fileLinkFormat; + $this->requestStack = $requestStack; + $this->baseDir = $baseDir; + $this->urlFormat = $urlFormat; + } + + public function format($file, $line) + { + if ($fmt = $this->getFileLinkFormat()) { + for ($i = 1; isset($fmt[$i]); ++$i) { + if (0 === strpos($file, $k = $fmt[$i++])) { + $file = substr_replace($file, $fmt[$i], 0, strlen($k)); + break; + } + } + + return strtr($fmt[0], array('%f' => $file, '%l' => $line)); + } + + return false; + } + + public function serialize() + { + return serialize($this->getFileLinkFormat()); + } + + public function unserialize($serialized) + { + if (\PHP_VERSION_ID >= 70000) { + $this->fileLinkFormat = unserialize($serialized, array('allowed_classes' => false)); + } else { + $this->fileLinkFormat = unserialize($serialized); + } + } + + private function getFileLinkFormat() + { + if ($this->fileLinkFormat) { + return $this->fileLinkFormat; + } + if ($this->requestStack && $this->baseDir && $this->urlFormat) { + $request = $this->requestStack->getMasterRequest(); + if ($request instanceof Request) { + return array( + $request->getSchemeAndHttpHost().$request->getBaseUrl().$this->urlFormat, + $this->baseDir.DIRECTORY_SEPARATOR, '', + ); + } + } + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/Debug/TraceableEventDispatcher.php b/lib/silex/vendor/symfony/http-kernel/Debug/TraceableEventDispatcher.php new file mode 100644 index 000000000..fbc49dffc --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Debug/TraceableEventDispatcher.php @@ -0,0 +1,82 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Debug; + +use Symfony\Component\EventDispatcher\Debug\TraceableEventDispatcher as BaseTraceableEventDispatcher; +use Symfony\Component\HttpKernel\KernelEvents; +use Symfony\Component\EventDispatcher\Event; + +/** + * Collects some data about event listeners. + * + * This event dispatcher delegates the dispatching to another one. + * + * @author Fabien Potencier + */ +class TraceableEventDispatcher extends BaseTraceableEventDispatcher +{ + /** + * {@inheritdoc} + */ + protected function preDispatch($eventName, Event $event) + { + switch ($eventName) { + case KernelEvents::REQUEST: + $this->stopwatch->openSection(); + break; + case KernelEvents::VIEW: + case KernelEvents::RESPONSE: + // stop only if a controller has been executed + if ($this->stopwatch->isStarted('controller')) { + $this->stopwatch->stop('controller'); + } + break; + case KernelEvents::TERMINATE: + $token = $event->getResponse()->headers->get('X-Debug-Token'); + // There is a very special case when using built-in AppCache class as kernel wrapper, in the case + // of an ESI request leading to a `stale` response [B] inside a `fresh` cached response [A]. + // In this case, `$token` contains the [B] debug token, but the open `stopwatch` section ID + // is equal to the [A] debug token. Trying to reopen section with the [B] token throws an exception + // which must be caught. + try { + $this->stopwatch->openSection($token); + } catch (\LogicException $e) { + } + break; + } + } + + /** + * {@inheritdoc} + */ + protected function postDispatch($eventName, Event $event) + { + switch ($eventName) { + case KernelEvents::CONTROLLER_ARGUMENTS: + $this->stopwatch->start('controller', 'section'); + break; + case KernelEvents::RESPONSE: + $token = $event->getResponse()->headers->get('X-Debug-Token'); + $this->stopwatch->stopSection($token); + break; + case KernelEvents::TERMINATE: + // In the special case described in the `preDispatch` method above, the `$token` section + // does not exist, then closing it throws an exception which must be caught. + $token = $event->getResponse()->headers->get('X-Debug-Token'); + try { + $this->stopwatch->stopSection($token); + } catch (\LogicException $e) { + } + break; + } + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/DependencyInjection/AddAnnotatedClassesToCachePass.php b/lib/silex/vendor/symfony/http-kernel/DependencyInjection/AddAnnotatedClassesToCachePass.php new file mode 100644 index 000000000..b1ecebdc9 --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/DependencyInjection/AddAnnotatedClassesToCachePass.php @@ -0,0 +1,153 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\DependencyInjection; + +use Composer\Autoload\ClassLoader; +use Symfony\Component\Debug\DebugClassLoader; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\HttpKernel\Kernel; + +/** + * Sets the classes to compile in the cache for the container. + * + * @author Fabien Potencier + */ +class AddAnnotatedClassesToCachePass implements CompilerPassInterface +{ + private $kernel; + + public function __construct(Kernel $kernel) + { + $this->kernel = $kernel; + } + + /** + * {@inheritdoc} + */ + public function process(ContainerBuilder $container) + { + $classes = array(); + $annotatedClasses = array(); + foreach ($container->getExtensions() as $extension) { + if ($extension instanceof Extension) { + if (\PHP_VERSION_ID < 70000) { + $classes = array_merge($classes, $extension->getClassesToCompile()); + } + $annotatedClasses = array_merge($annotatedClasses, $extension->getAnnotatedClassesToCompile()); + } + } + + $existingClasses = $this->getClassesInComposerClassMaps(); + + if (\PHP_VERSION_ID < 70000) { + $classes = $container->getParameterBag()->resolveValue($classes); + $this->kernel->setClassCache($this->expandClasses($classes, $existingClasses)); + } + $annotatedClasses = $container->getParameterBag()->resolveValue($annotatedClasses); + $this->kernel->setAnnotatedClassCache($this->expandClasses($annotatedClasses, $existingClasses)); + } + + /** + * Expands the given class patterns using a list of existing classes. + * + * @param array $patterns The class patterns to expand + * @param array $classes The existing classes to match against the patterns + * + * @return array A list of classes derivated from the patterns + */ + private function expandClasses(array $patterns, array $classes) + { + $expanded = array(); + + // Explicit classes declared in the patterns are returned directly + foreach ($patterns as $key => $pattern) { + if ('\\' !== substr($pattern, -1) && false === strpos($pattern, '*')) { + unset($patterns[$key]); + $expanded[] = ltrim($pattern, '\\'); + } + } + + // Match patterns with the classes list + $regexps = $this->patternsToRegexps($patterns); + + foreach ($classes as $class) { + $class = ltrim($class, '\\'); + + if ($this->matchAnyRegexps($class, $regexps)) { + $expanded[] = $class; + } + } + + return array_unique($expanded); + } + + private function getClassesInComposerClassMaps() + { + $classes = array(); + + foreach (spl_autoload_functions() as $function) { + if (!is_array($function)) { + continue; + } + + if ($function[0] instanceof DebugClassLoader) { + $function = $function[0]->getClassLoader(); + } + + if (is_array($function) && $function[0] instanceof ClassLoader) { + $classes += array_filter($function[0]->getClassMap()); + } + } + + return array_keys($classes); + } + + private function patternsToRegexps($patterns) + { + $regexps = array(); + + foreach ($patterns as $pattern) { + // Escape user input + $regex = preg_quote(ltrim($pattern, '\\')); + + // Wildcards * and ** + $regex = strtr($regex, array('\\*\\*' => '.*?', '\\*' => '[^\\\\]*?')); + + // If this class does not end by a slash, anchor the end + if ('\\' !== substr($regex, -1)) { + $regex .= '$'; + } + + $regexps[] = '{^\\\\'.$regex.'}'; + } + + return $regexps; + } + + private function matchAnyRegexps($class, $regexps) + { + $blacklisted = false !== strpos($class, 'Test'); + + foreach ($regexps as $regex) { + if ($blacklisted && false === strpos($regex, 'Test')) { + continue; + } + + if (preg_match($regex, '\\'.$class)) { + return true; + } + } + + return false; + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/DependencyInjection/AddClassesToCachePass.php b/lib/silex/vendor/symfony/http-kernel/DependencyInjection/AddClassesToCachePass.php new file mode 100644 index 000000000..8ae78e0d8 --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/DependencyInjection/AddClassesToCachePass.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\HttpKernel\DependencyInjection; + +@trigger_error('The '.__NAMESPACE__.'\AddClassesToCachePass class is deprecated since Symfony 3.3 and will be removed in 4.0.', E_USER_DEPRECATED); + +/** + * Sets the classes to compile in the cache for the container. + * + * @author Fabien Potencier + * + * @deprecated since version 3.3, to be removed in 4.0. + */ +class AddClassesToCachePass extends AddAnnotatedClassesToCachePass +{ +} diff --git a/lib/silex/vendor/symfony/http-kernel/DependencyInjection/ConfigurableExtension.php b/lib/silex/vendor/symfony/http-kernel/DependencyInjection/ConfigurableExtension.php new file mode 100644 index 000000000..072c35f1c --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/DependencyInjection/ConfigurableExtension.php @@ -0,0 +1,42 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\DependencyInjection; + +use Symfony\Component\DependencyInjection\ContainerBuilder; + +/** + * This extension sub-class provides first-class integration with the + * Config/Definition Component. + * + * You can use this as base class if + * + * a) you use the Config/Definition component for configuration, + * b) your configuration class is named "Configuration", and + * c) the configuration class resides in the DependencyInjection sub-folder. + * + * @author Johannes M. Schmitt + */ +abstract class ConfigurableExtension extends Extension +{ + /** + * {@inheritdoc} + */ + final public function load(array $configs, ContainerBuilder $container) + { + $this->loadInternal($this->processConfiguration($this->getConfiguration($configs, $container), $configs), $container); + } + + /** + * Configures the passed container according to the merged configuration. + */ + abstract protected function loadInternal(array $mergedConfig, ContainerBuilder $container); +} diff --git a/lib/silex/vendor/symfony/http-kernel/DependencyInjection/ControllerArgumentValueResolverPass.php b/lib/silex/vendor/symfony/http-kernel/DependencyInjection/ControllerArgumentValueResolverPass.php new file mode 100644 index 000000000..343e217b9 --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/DependencyInjection/ControllerArgumentValueResolverPass.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\HttpKernel\DependencyInjection; + +use Symfony\Component\DependencyInjection\Argument\IteratorArgument; +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\Compiler\PriorityTaggedServiceTrait; +use Symfony\Component\DependencyInjection\ContainerBuilder; + +/** + * Gathers and configures the argument value resolvers. + * + * @author Iltar van der Berg + */ +class ControllerArgumentValueResolverPass implements CompilerPassInterface +{ + use PriorityTaggedServiceTrait; + + private $argumentResolverService; + private $argumentValueResolverTag; + + public function __construct($argumentResolverService = 'argument_resolver', $argumentValueResolverTag = 'controller.argument_value_resolver') + { + $this->argumentResolverService = $argumentResolverService; + $this->argumentValueResolverTag = $argumentValueResolverTag; + } + + public function process(ContainerBuilder $container) + { + if (!$container->hasDefinition($this->argumentResolverService)) { + return; + } + + $container + ->getDefinition($this->argumentResolverService) + ->replaceArgument(1, new IteratorArgument($this->findAndSortTaggedServices($this->argumentValueResolverTag, $container))) + ; + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/DependencyInjection/Extension.php b/lib/silex/vendor/symfony/http-kernel/DependencyInjection/Extension.php new file mode 100644 index 000000000..a382c15d6 --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/DependencyInjection/Extension.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\HttpKernel\DependencyInjection; + +use Symfony\Component\DependencyInjection\Extension\Extension as BaseExtension; + +/** + * Allow adding classes to the class cache. + * + * @author Fabien Potencier + */ +abstract class Extension extends BaseExtension +{ + private $classes = array(); + private $annotatedClasses = array(); + + /** + * Gets the classes to cache. + * + * @return array An array of classes + * + * @deprecated since version 3.3, to be removed in 4.0. + */ + public function getClassesToCompile() + { + if (\PHP_VERSION_ID >= 70000) { + @trigger_error(__METHOD__.'() is deprecated since Symfony 3.3, to be removed in 4.0.', E_USER_DEPRECATED); + } + + return $this->classes; + } + + /** + * Gets the annotated classes to cache. + * + * @return array An array of classes + */ + public function getAnnotatedClassesToCompile() + { + return $this->annotatedClasses; + } + + /** + * Adds classes to the class cache. + * + * @param array $classes An array of class patterns + * + * @deprecated since version 3.3, to be removed in 4.0. + */ + public function addClassesToCompile(array $classes) + { + if (\PHP_VERSION_ID >= 70000) { + @trigger_error(__METHOD__.'() is deprecated since Symfony 3.3, to be removed in 4.0.', E_USER_DEPRECATED); + } + + $this->classes = array_merge($this->classes, $classes); + } + + /** + * Adds annotated classes to the class cache. + * + * @param array $annotatedClasses An array of class patterns + */ + public function addAnnotatedClassesToCompile(array $annotatedClasses) + { + $this->annotatedClasses = array_merge($this->annotatedClasses, $annotatedClasses); + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/DependencyInjection/FragmentRendererPass.php b/lib/silex/vendor/symfony/http-kernel/DependencyInjection/FragmentRendererPass.php new file mode 100644 index 000000000..ac52c8732 --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/DependencyInjection/FragmentRendererPass.php @@ -0,0 +1,67 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\DependencyInjection; + +use Symfony\Component\DependencyInjection\Compiler\ServiceLocatorTagPass; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\HttpKernel\Fragment\FragmentRendererInterface; + +/** + * Adds services tagged kernel.fragment_renderer as HTTP content rendering strategies. + * + * @author Fabien Potencier + */ +class FragmentRendererPass implements CompilerPassInterface +{ + private $handlerService; + private $rendererTag; + + /** + * @param string $handlerService Service name of the fragment handler in the container + * @param string $rendererTag Tag name used for fragments + */ + public function __construct($handlerService = 'fragment.handler', $rendererTag = 'kernel.fragment_renderer') + { + $this->handlerService = $handlerService; + $this->rendererTag = $rendererTag; + } + + public function process(ContainerBuilder $container) + { + if (!$container->hasDefinition($this->handlerService)) { + return; + } + + $definition = $container->getDefinition($this->handlerService); + $renderers = array(); + foreach ($container->findTaggedServiceIds($this->rendererTag, true) as $id => $tags) { + $def = $container->getDefinition($id); + $class = $container->getParameterBag()->resolveValue($def->getClass()); + + if (!$r = $container->getReflectionClass($class)) { + throw new InvalidArgumentException(sprintf('Class "%s" used for service "%s" cannot be found.', $class, $id)); + } + if (!$r->isSubclassOf(FragmentRendererInterface::class)) { + throw new InvalidArgumentException(sprintf('Service "%s" must implement interface "%s".', $id, FragmentRendererInterface::class)); + } + + foreach ($tags as $tag) { + $renderers[$tag['alias']] = new Reference($id); + } + } + + $definition->replaceArgument(0, ServiceLocatorTagPass::register($container, $renderers)); + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/DependencyInjection/LazyLoadingFragmentHandler.php b/lib/silex/vendor/symfony/http-kernel/DependencyInjection/LazyLoadingFragmentHandler.php new file mode 100644 index 000000000..00b05959c --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/DependencyInjection/LazyLoadingFragmentHandler.php @@ -0,0 +1,79 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\DependencyInjection; + +use Psr\Container\ContainerInterface; +use Symfony\Component\HttpFoundation\RequestStack; +use Symfony\Component\HttpKernel\Fragment\FragmentHandler; + +/** + * Lazily loads fragment renderers from the dependency injection container. + * + * @author Fabien Potencier + */ +class LazyLoadingFragmentHandler extends FragmentHandler +{ + private $container; + /** + * @deprecated since version 3.3, to be removed in 4.0 + */ + private $rendererIds = array(); + private $initialized = array(); + + /** + * @param ContainerInterface $container A container + * @param RequestStack $requestStack The Request stack that controls the lifecycle of requests + * @param bool $debug Whether the debug mode is enabled or not + */ + public function __construct(ContainerInterface $container, RequestStack $requestStack, $debug = false) + { + $this->container = $container; + + parent::__construct($requestStack, array(), $debug); + } + + /** + * Adds a service as a fragment renderer. + * + * @param string $name The service name + * @param string $renderer The render service id + * + * @deprecated since version 3.3, to be removed in 4.0 + */ + public function addRendererService($name, $renderer) + { + @trigger_error(sprintf('The %s() method is deprecated since Symfony 3.3 and will be removed in 4.0.', __METHOD__), E_USER_DEPRECATED); + + $this->rendererIds[$name] = $renderer; + } + + /** + * {@inheritdoc} + */ + public function render($uri, $renderer = 'inline', array $options = array()) + { + // BC 3.x, to be removed in 4.0 + if (isset($this->rendererIds[$renderer])) { + $this->addRenderer($this->container->get($this->rendererIds[$renderer])); + unset($this->rendererIds[$renderer]); + + return parent::render($uri, $renderer, $options); + } + + if (!isset($this->initialized[$renderer]) && $this->container->has($renderer)) { + $this->addRenderer($this->container->get($renderer)); + $this->initialized[$renderer] = true; + } + + return parent::render($uri, $renderer, $options); + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/DependencyInjection/LoggerPass.php b/lib/silex/vendor/symfony/http-kernel/DependencyInjection/LoggerPass.php new file mode 100644 index 000000000..2ad7f3222 --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/DependencyInjection/LoggerPass.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\HttpKernel\DependencyInjection; + +use Psr\Log\LoggerInterface; +use Symfony\Component\HttpKernel\Log\Logger; +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\ContainerBuilder; + +/** + * Registers the default logger if necessary. + * + * @author Kévin Dunglas + */ +class LoggerPass implements CompilerPassInterface +{ + /** + * {@inheritdoc} + */ + public function process(ContainerBuilder $container) + { + $container->setAlias(LoggerInterface::class, 'logger') + ->setPublic(false); + + if ($container->has('logger')) { + return; + } + + $container->register('logger', Logger::class) + ->setPublic(false); + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/DependencyInjection/MergeExtensionConfigurationPass.php b/lib/silex/vendor/symfony/http-kernel/DependencyInjection/MergeExtensionConfigurationPass.php new file mode 100644 index 000000000..dcd73828d --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/DependencyInjection/MergeExtensionConfigurationPass.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\HttpKernel\DependencyInjection; + +use Symfony\Component\DependencyInjection\Compiler\MergeExtensionConfigurationPass as BaseMergeExtensionConfigurationPass; +use Symfony\Component\DependencyInjection\ContainerBuilder; + +/** + * Ensures certain extensions are always loaded. + * + * @author Kris Wallsmith + */ +class MergeExtensionConfigurationPass extends BaseMergeExtensionConfigurationPass +{ + private $extensions; + + public function __construct(array $extensions) + { + $this->extensions = $extensions; + } + + public function process(ContainerBuilder $container) + { + foreach ($this->extensions as $extension) { + if (!count($container->getExtensionConfig($extension))) { + $container->loadFromExtension($extension, array()); + } + } + + parent::process($container); + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/DependencyInjection/RegisterControllerArgumentLocatorsPass.php b/lib/silex/vendor/symfony/http-kernel/DependencyInjection/RegisterControllerArgumentLocatorsPass.php new file mode 100644 index 000000000..985dfb71d --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/DependencyInjection/RegisterControllerArgumentLocatorsPass.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\HttpKernel\DependencyInjection; + +use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; +use Symfony\Component\DependencyInjection\ChildDefinition; +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\Compiler\ServiceLocatorTagPass; +use Symfony\Component\DependencyInjection\ContainerAwareInterface; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; +use Symfony\Component\DependencyInjection\LazyProxy\ProxyHelper; +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\DependencyInjection\TypedReference; + +/** + * Creates the service-locators required by ServiceValueResolver. + * + * @author Nicolas Grekas + */ +class RegisterControllerArgumentLocatorsPass implements CompilerPassInterface +{ + private $resolverServiceId; + private $controllerTag; + + public function __construct($resolverServiceId = 'argument_resolver.service', $controllerTag = 'controller.service_arguments') + { + $this->resolverServiceId = $resolverServiceId; + $this->controllerTag = $controllerTag; + } + + public function process(ContainerBuilder $container) + { + if (false === $container->hasDefinition($this->resolverServiceId)) { + return; + } + + $parameterBag = $container->getParameterBag(); + $controllers = array(); + + foreach ($container->findTaggedServiceIds($this->controllerTag, true) as $id => $tags) { + $def = $container->getDefinition($id); + $def->setPublic(true); + $class = $def->getClass(); + $autowire = $def->isAutowired(); + $bindings = $def->getBindings(); + + // resolve service class, taking parent definitions into account + while ($def instanceof ChildDefinition) { + $def = $container->findDefinition($def->getParent()); + $class = $class ?: $def->getClass(); + $bindings = $def->getBindings(); + } + $class = $parameterBag->resolveValue($class); + + if (!$r = $container->getReflectionClass($class)) { + throw new InvalidArgumentException(sprintf('Class "%s" used for service "%s" cannot be found.', $class, $id)); + } + $isContainerAware = $r->implementsInterface(ContainerAwareInterface::class) || is_subclass_of($class, AbstractController::class); + + // get regular public methods + $methods = array(); + $arguments = array(); + foreach ($r->getMethods(\ReflectionMethod::IS_PUBLIC) as $r) { + if ('setContainer' === $r->name && $isContainerAware) { + continue; + } + if (!$r->isConstructor() && !$r->isDestructor() && !$r->isAbstract()) { + $methods[strtolower($r->name)] = array($r, $r->getParameters()); + } + } + + // validate and collect explicit per-actions and per-arguments service references + foreach ($tags as $attributes) { + if (!isset($attributes['action']) && !isset($attributes['argument']) && !isset($attributes['id'])) { + $autowire = true; + continue; + } + foreach (array('action', 'argument', 'id') as $k) { + if (!isset($attributes[$k][0])) { + throw new InvalidArgumentException(sprintf('Missing "%s" attribute on tag "%s" %s for service "%s".', $k, $this->controllerTag, json_encode($attributes, JSON_UNESCAPED_UNICODE), $id)); + } + } + if (!isset($methods[$action = strtolower($attributes['action'])])) { + throw new InvalidArgumentException(sprintf('Invalid "action" attribute on tag "%s" for service "%s": no public "%s()" method found on class "%s".', $this->controllerTag, $id, $attributes['action'], $class)); + } + list($r, $parameters) = $methods[$action]; + $found = false; + + foreach ($parameters as $p) { + if ($attributes['argument'] === $p->name) { + if (!isset($arguments[$r->name][$p->name])) { + $arguments[$r->name][$p->name] = $attributes['id']; + } + $found = true; + break; + } + } + + if (!$found) { + throw new InvalidArgumentException(sprintf('Invalid "%s" tag for service "%s": method "%s()" has no "%s" argument on class "%s".', $this->controllerTag, $id, $r->name, $attributes['argument'], $class)); + } + } + + foreach ($methods as list($r, $parameters)) { + /** @var \ReflectionMethod $r */ + + // create a per-method map of argument-names to service/type-references + $args = array(); + foreach ($parameters as $p) { + /** @var \ReflectionParameter $p */ + $type = $target = ProxyHelper::getTypeHint($r, $p, true); + $invalidBehavior = ContainerInterface::IGNORE_ON_INVALID_REFERENCE; + + if (isset($arguments[$r->name][$p->name])) { + $target = $arguments[$r->name][$p->name]; + if ('?' !== $target[0]) { + $invalidBehavior = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE; + } elseif ('' === $target = (string) substr($target, 1)) { + throw new InvalidArgumentException(sprintf('A "%s" tag must have non-empty "id" attributes for service "%s".', $this->controllerTag, $id)); + } elseif ($p->allowsNull() && !$p->isOptional()) { + $invalidBehavior = ContainerInterface::NULL_ON_INVALID_REFERENCE; + } + } elseif (isset($bindings[$bindingName = '$'.$p->name]) || isset($bindings[$bindingName = $type])) { + $binding = $bindings[$bindingName]; + + list($bindingValue, $bindingId) = $binding->getValues(); + + if (!$bindingValue instanceof Reference) { + continue; + } + + $binding->setValues(array($bindingValue, $bindingId, true)); + $args[$p->name] = $bindingValue; + + continue; + } elseif (!$type || !$autowire) { + continue; + } + + if ($type && !$p->isOptional() && !$p->allowsNull() && !class_exists($type) && !interface_exists($type, false)) { + $message = sprintf('Cannot determine controller argument for "%s::%s()": the $%s argument is type-hinted with the non-existent class or interface: "%s".', $class, $r->name, $p->name, $type); + + // see if the type-hint lives in the same namespace as the controller + if (0 === strncmp($type, $class, strrpos($class, '\\'))) { + $message .= ' Did you forget to add a use statement?'; + } + + throw new InvalidArgumentException($message); + } + + $args[$p->name] = $type ? new TypedReference($target, $type, $r->class, $invalidBehavior) : new Reference($target, $invalidBehavior); + } + // register the maps as a per-method service-locators + if ($args) { + $controllers[$id.':'.$r->name] = ServiceLocatorTagPass::register($container, $args); + } + } + } + + $container->getDefinition($this->resolverServiceId) + ->replaceArgument(0, ServiceLocatorTagPass::register($container, $controllers)); + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/DependencyInjection/RemoveEmptyControllerArgumentLocatorsPass.php b/lib/silex/vendor/symfony/http-kernel/DependencyInjection/RemoveEmptyControllerArgumentLocatorsPass.php new file mode 100644 index 000000000..ab50cec35 --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/DependencyInjection/RemoveEmptyControllerArgumentLocatorsPass.php @@ -0,0 +1,76 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\DependencyInjection; + +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\ContainerBuilder; + +/** + * Removes empty service-locators registered for ServiceValueResolver. + * + * @author Nicolas Grekas + */ +class RemoveEmptyControllerArgumentLocatorsPass implements CompilerPassInterface +{ + private $resolverServiceId; + + public function __construct($resolverServiceId = 'argument_resolver.service') + { + $this->resolverServiceId = $resolverServiceId; + } + + public function process(ContainerBuilder $container) + { + if (false === $container->hasDefinition($this->resolverServiceId)) { + return; + } + + $serviceResolver = $container->getDefinition($this->resolverServiceId); + $controllerLocator = $container->getDefinition((string) $serviceResolver->getArgument(0)); + $controllers = $controllerLocator->getArgument(0); + + foreach ($controllers as $controller => $argumentRef) { + $argumentLocator = $container->getDefinition((string) $argumentRef->getValues()[0]); + + if (!$argumentLocator->getArgument(0)) { + // remove empty argument locators + $reason = sprintf('Removing service-argument resolver for controller "%s": no corresponding services exist for the referenced types.', $controller); + } else { + // any methods listed for call-at-instantiation cannot be actions + $reason = false; + $action = substr(strrchr($controller, ':'), 1); + $id = substr($controller, 0, -1 - strlen($action)); + $controllerDef = $container->getDefinition($id); + foreach ($controllerDef->getMethodCalls() as list($method)) { + if (0 === strcasecmp($action, $method)) { + $reason = sprintf('Removing method "%s" of service "%s" from controller candidates: the method is called at instantiation, thus cannot be an action.', $action, $id); + break; + } + } + if (!$reason) { + if ($controllerDef->getClass() === $id) { + $controllers[$id.'::'.$action] = $argumentRef; + } + if ('__invoke' === $action) { + $controllers[$id] = $argumentRef; + } + continue; + } + } + + unset($controllers[$controller]); + $container->log($this, $reason); + } + + $controllerLocator->replaceArgument(0, $controllers); + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/DependencyInjection/ResettableServicePass.php b/lib/silex/vendor/symfony/http-kernel/DependencyInjection/ResettableServicePass.php new file mode 100644 index 000000000..29433a6d5 --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/DependencyInjection/ResettableServicePass.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\HttpKernel\DependencyInjection; + +use Symfony\Component\DependencyInjection\Argument\IteratorArgument; +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\DependencyInjection\Exception\RuntimeException; +use Symfony\Component\DependencyInjection\Reference; + +/** + * @author Alexander M. Turek + */ +class ResettableServicePass implements CompilerPassInterface +{ + private $tagName; + + public function __construct($tagName = 'kernel.reset') + { + $this->tagName = $tagName; + } + + /** + * {@inheritdoc} + */ + public function process(ContainerBuilder $container) + { + if (!$container->has('services_resetter')) { + return; + } + + $services = $methods = array(); + + foreach ($container->findTaggedServiceIds($this->tagName, true) as $id => $tags) { + $services[$id] = new Reference($id, ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE); + $attributes = $tags[0]; + + if (!isset($attributes['method'])) { + throw new RuntimeException(sprintf('Tag %s requires the "method" attribute to be set.', $this->tagName)); + } + + $methods[$id] = $attributes['method']; + } + + if (empty($services)) { + $container->removeAlias('services_resetter'); + $container->removeDefinition('services_resetter'); + + return; + } + + $container->findDefinition('services_resetter') + ->setArgument(0, new IteratorArgument($services)) + ->setArgument(1, $methods); + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/DependencyInjection/ServicesResetter.php b/lib/silex/vendor/symfony/http-kernel/DependencyInjection/ServicesResetter.php new file mode 100644 index 000000000..b82d2fef3 --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/DependencyInjection/ServicesResetter.php @@ -0,0 +1,39 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\DependencyInjection; + +/** + * Resets provided services. + * + * @author Alexander M. Turek + * @author Nicolas Grekas + * + * @internal + */ +class ServicesResetter +{ + private $resettableServices; + private $resetMethods; + + public function __construct(\Traversable $resettableServices, array $resetMethods) + { + $this->resettableServices = $resettableServices; + $this->resetMethods = $resetMethods; + } + + public function reset() + { + foreach ($this->resettableServices as $id => $service) { + $service->{$this->resetMethods[$id]}(); + } + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/Event/FilterControllerArgumentsEvent.php b/lib/silex/vendor/symfony/http-kernel/Event/FilterControllerArgumentsEvent.php new file mode 100644 index 000000000..2b08f2d77 --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Event/FilterControllerArgumentsEvent.php @@ -0,0 +1,52 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Event; + +use Symfony\Component\HttpKernel\HttpKernelInterface; +use Symfony\Component\HttpFoundation\Request; + +/** + * Allows filtering of controller arguments. + * + * You can call getController() to retrieve the controller and getArguments + * to retrieve the current arguments. With setArguments() you can replace + * arguments that are used to call the controller. + * + * Arguments set in the event must be compatible with the signature of the + * controller. + * + * @author Christophe Coevoet + */ +class FilterControllerArgumentsEvent extends FilterControllerEvent +{ + private $arguments; + + public function __construct(HttpKernelInterface $kernel, callable $controller, array $arguments, Request $request, $requestType) + { + parent::__construct($kernel, $controller, $request, $requestType); + + $this->arguments = $arguments; + } + + /** + * @return array + */ + public function getArguments() + { + return $this->arguments; + } + + public function setArguments(array $arguments) + { + $this->arguments = $arguments; + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/Event/FilterControllerEvent.php b/lib/silex/vendor/symfony/http-kernel/Event/FilterControllerEvent.php new file mode 100644 index 000000000..84cbc2eaf --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Event/FilterControllerEvent.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\HttpKernel\Event; + +use Symfony\Component\HttpKernel\HttpKernelInterface; +use Symfony\Component\HttpFoundation\Request; + +/** + * Allows filtering of a controller callable. + * + * You can call getController() to retrieve the current controller. With + * setController() you can set a new controller that is used in the processing + * of the request. + * + * Controllers should be callables. + * + * @author Bernhard Schussek + */ +class FilterControllerEvent extends KernelEvent +{ + private $controller; + + public function __construct(HttpKernelInterface $kernel, callable $controller, Request $request, $requestType) + { + parent::__construct($kernel, $request, $requestType); + + $this->setController($controller); + } + + /** + * Returns the current controller. + * + * @return callable + */ + public function getController() + { + return $this->controller; + } + + public function setController(callable $controller) + { + $this->controller = $controller; + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/Event/FilterResponseEvent.php b/lib/silex/vendor/symfony/http-kernel/Event/FilterResponseEvent.php new file mode 100644 index 000000000..53a7efce7 --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Event/FilterResponseEvent.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\HttpKernel\Event; + +use Symfony\Component\HttpKernel\HttpKernelInterface; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +/** + * Allows to filter a Response object. + * + * You can call getResponse() to retrieve the current response. With + * setResponse() you can set a new response that will be returned to the + * browser. + * + * @author Bernhard Schussek + */ +class FilterResponseEvent extends KernelEvent +{ + private $response; + + public function __construct(HttpKernelInterface $kernel, Request $request, $requestType, Response $response) + { + parent::__construct($kernel, $request, $requestType); + + $this->setResponse($response); + } + + /** + * Returns the current response object. + * + * @return Response + */ + public function getResponse() + { + return $this->response; + } + + /** + * Sets a new response object. + */ + public function setResponse(Response $response) + { + $this->response = $response; + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/Event/FinishRequestEvent.php b/lib/silex/vendor/symfony/http-kernel/Event/FinishRequestEvent.php new file mode 100644 index 000000000..ee724843c --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Event/FinishRequestEvent.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\HttpKernel\Event; + +/** + * Triggered whenever a request is fully processed. + * + * @author Benjamin Eberlei + */ +class FinishRequestEvent extends KernelEvent +{ +} diff --git a/lib/silex/vendor/symfony/http-kernel/Event/GetResponseEvent.php b/lib/silex/vendor/symfony/http-kernel/Event/GetResponseEvent.php new file mode 100644 index 000000000..f7745ea3d --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Event/GetResponseEvent.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\HttpKernel\Event; + +use Symfony\Component\HttpFoundation\Response; + +/** + * Allows to create a response for a request. + * + * Call setResponse() to set the response that will be returned for the + * current request. The propagation of this event is stopped as soon as a + * response is set. + * + * @author Bernhard Schussek + */ +class GetResponseEvent extends KernelEvent +{ + private $response; + + /** + * Returns the response object. + * + * @return Response + */ + public function getResponse() + { + return $this->response; + } + + /** + * Sets a response and stops event propagation. + */ + public function setResponse(Response $response) + { + $this->response = $response; + + $this->stopPropagation(); + } + + /** + * Returns whether a response was set. + * + * @return bool Whether a response was set + */ + public function hasResponse() + { + return null !== $this->response; + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/Event/GetResponseForControllerResultEvent.php b/lib/silex/vendor/symfony/http-kernel/Event/GetResponseForControllerResultEvent.php new file mode 100644 index 000000000..f70ce09e0 --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Event/GetResponseForControllerResultEvent.php @@ -0,0 +1,61 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Event; + +use Symfony\Component\HttpKernel\HttpKernelInterface; +use Symfony\Component\HttpFoundation\Request; + +/** + * Allows to create a response for the return value of a controller. + * + * Call setResponse() to set the response that will be returned for the + * current request. The propagation of this event is stopped as soon as a + * response is set. + * + * @author Bernhard Schussek + */ +class GetResponseForControllerResultEvent extends GetResponseEvent +{ + /** + * The return value of the controller. + * + * @var mixed + */ + private $controllerResult; + + public function __construct(HttpKernelInterface $kernel, Request $request, $requestType, $controllerResult) + { + parent::__construct($kernel, $request, $requestType); + + $this->controllerResult = $controllerResult; + } + + /** + * Returns the return value of the controller. + * + * @return mixed The controller return value + */ + public function getControllerResult() + { + return $this->controllerResult; + } + + /** + * Assigns the return value of the controller. + * + * @param mixed $controllerResult The controller return value + */ + public function setControllerResult($controllerResult) + { + $this->controllerResult = $controllerResult; + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/Event/GetResponseForExceptionEvent.php b/lib/silex/vendor/symfony/http-kernel/Event/GetResponseForExceptionEvent.php new file mode 100644 index 000000000..751b74515 --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Event/GetResponseForExceptionEvent.php @@ -0,0 +1,90 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Event; + +use Symfony\Component\HttpKernel\HttpKernelInterface; +use Symfony\Component\HttpFoundation\Request; + +/** + * Allows to create a response for a thrown exception. + * + * Call setResponse() to set the response that will be returned for the + * current request. The propagation of this event is stopped as soon as a + * response is set. + * + * You can also call setException() to replace the thrown exception. This + * exception will be thrown if no response is set during processing of this + * event. + * + * @author Bernhard Schussek + */ +class GetResponseForExceptionEvent extends GetResponseEvent +{ + /** + * The exception object. + * + * @var \Exception + */ + private $exception; + + /** + * @var bool + */ + private $allowCustomResponseCode = false; + + public function __construct(HttpKernelInterface $kernel, Request $request, $requestType, \Exception $e) + { + parent::__construct($kernel, $request, $requestType); + + $this->setException($e); + } + + /** + * Returns the thrown exception. + * + * @return \Exception The thrown exception + */ + public function getException() + { + return $this->exception; + } + + /** + * Replaces the thrown exception. + * + * This exception will be thrown if no response is set in the event. + * + * @param \Exception $exception The thrown exception + */ + public function setException(\Exception $exception) + { + $this->exception = $exception; + } + + /** + * Mark the event as allowing a custom response code. + */ + public function allowCustomResponseCode() + { + $this->allowCustomResponseCode = true; + } + + /** + * Returns true if the event allows a custom response code. + * + * @return bool + */ + public function isAllowingCustomResponseCode() + { + return $this->allowCustomResponseCode; + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/Event/KernelEvent.php b/lib/silex/vendor/symfony/http-kernel/Event/KernelEvent.php new file mode 100644 index 000000000..992f6b4dc --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Event/KernelEvent.php @@ -0,0 +1,82 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Event; + +use Symfony\Component\HttpKernel\HttpKernelInterface; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\EventDispatcher\Event; + +/** + * Base class for events thrown in the HttpKernel component. + * + * @author Bernhard Schussek + */ +class KernelEvent extends Event +{ + private $kernel; + private $request; + private $requestType; + + /** + * @param HttpKernelInterface $kernel The kernel in which this event was thrown + * @param Request $request The request the kernel is currently processing + * @param int $requestType The request type the kernel is currently processing; one of + * HttpKernelInterface::MASTER_REQUEST or HttpKernelInterface::SUB_REQUEST + */ + public function __construct(HttpKernelInterface $kernel, Request $request, $requestType) + { + $this->kernel = $kernel; + $this->request = $request; + $this->requestType = $requestType; + } + + /** + * Returns the kernel in which this event was thrown. + * + * @return HttpKernelInterface + */ + public function getKernel() + { + return $this->kernel; + } + + /** + * Returns the request the kernel is currently processing. + * + * @return Request + */ + public function getRequest() + { + return $this->request; + } + + /** + * Returns the request type the kernel is currently processing. + * + * @return int One of HttpKernelInterface::MASTER_REQUEST and + * HttpKernelInterface::SUB_REQUEST + */ + public function getRequestType() + { + return $this->requestType; + } + + /** + * Checks if this is a master request. + * + * @return bool True if the request is a master request + */ + public function isMasterRequest() + { + return HttpKernelInterface::MASTER_REQUEST === $this->requestType; + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/Event/PostResponseEvent.php b/lib/silex/vendor/symfony/http-kernel/Event/PostResponseEvent.php new file mode 100644 index 000000000..2406fddbe --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Event/PostResponseEvent.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\HttpKernel\Event; + +use Symfony\Component\HttpKernel\HttpKernelInterface; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +/** + * Allows to execute logic after a response was sent. + * + * Since it's only triggered on master requests, the `getRequestType()` method + * will always return the value of `HttpKernelInterface::MASTER_REQUEST`. + * + * @author Jordi Boggiano + */ +class PostResponseEvent extends KernelEvent +{ + private $response; + + public function __construct(HttpKernelInterface $kernel, Request $request, Response $response) + { + parent::__construct($kernel, $request, HttpKernelInterface::MASTER_REQUEST); + + $this->response = $response; + } + + /** + * Returns the response for which this event was thrown. + * + * @return Response + */ + public function getResponse() + { + return $this->response; + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/EventListener/AbstractSessionListener.php b/lib/silex/vendor/symfony/http-kernel/EventListener/AbstractSessionListener.php new file mode 100644 index 000000000..dff29ee80 --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/EventListener/AbstractSessionListener.php @@ -0,0 +1,76 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\EventListener; + +use Symfony\Component\HttpFoundation\Session\Session; +use Symfony\Component\HttpFoundation\Session\SessionInterface; +use Symfony\Component\HttpKernel\Event\FilterResponseEvent; +use Symfony\Component\HttpKernel\Event\GetResponseEvent; +use Symfony\Component\HttpKernel\KernelEvents; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; + +/** + * Sets the session in the request. + * + * @author Johannes M. Schmitt + */ +abstract class AbstractSessionListener implements EventSubscriberInterface +{ + public function onKernelRequest(GetResponseEvent $event) + { + if (!$event->isMasterRequest()) { + return; + } + + $request = $event->getRequest(); + $session = $this->getSession(); + if (null === $session || $request->hasSession()) { + return; + } + + $request->setSession($session); + } + + public function onKernelResponse(FilterResponseEvent $event) + { + if (!$event->isMasterRequest()) { + return; + } + + if (!$session = $event->getRequest()->getSession()) { + return; + } + + if ($session->isStarted() || ($session instanceof Session && $session->hasBeenStarted())) { + $event->getResponse() + ->setPrivate() + ->setMaxAge(0) + ->headers->addCacheControlDirective('must-revalidate'); + } + } + + public static function getSubscribedEvents() + { + return array( + KernelEvents::REQUEST => array('onKernelRequest', 128), + // low priority to come after regular response listeners, same as SaveSessionListener + KernelEvents::RESPONSE => array('onKernelResponse', -1000), + ); + } + + /** + * Gets the session object. + * + * @return SessionInterface|null A SessionInterface instance or null if no session is available + */ + abstract protected function getSession(); +} diff --git a/lib/silex/vendor/symfony/http-kernel/EventListener/AbstractTestSessionListener.php b/lib/silex/vendor/symfony/http-kernel/EventListener/AbstractTestSessionListener.php new file mode 100644 index 000000000..5f0ea5c0a --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/EventListener/AbstractTestSessionListener.php @@ -0,0 +1,89 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\EventListener; + +use Symfony\Component\HttpFoundation\Cookie; +use Symfony\Component\HttpFoundation\Session\Session; +use Symfony\Component\HttpFoundation\Session\SessionInterface; +use Symfony\Component\HttpKernel\KernelEvents; +use Symfony\Component\HttpKernel\Event\FilterResponseEvent; +use Symfony\Component\HttpKernel\Event\GetResponseEvent; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; + +/** + * TestSessionListener. + * + * Saves session in test environment. + * + * @author Bulat Shakirzyanov + * @author Fabien Potencier + */ +abstract class AbstractTestSessionListener implements EventSubscriberInterface +{ + public function onKernelRequest(GetResponseEvent $event) + { + if (!$event->isMasterRequest()) { + return; + } + + // bootstrap the session + $session = $this->getSession(); + if (!$session) { + return; + } + + $cookies = $event->getRequest()->cookies; + + if ($cookies->has($session->getName())) { + $session->setId($cookies->get($session->getName())); + } + } + + /** + * Checks if session was initialized and saves if current request is master + * Runs on 'kernel.response' in test environment. + */ + public function onKernelResponse(FilterResponseEvent $event) + { + if (!$event->isMasterRequest()) { + return; + } + + if (!$session = $event->getRequest()->getSession()) { + return; + } + + if ($wasStarted = $session->isStarted()) { + $session->save(); + } + + if ($session instanceof Session ? !$session->isEmpty() : $wasStarted) { + $params = session_get_cookie_params(); + $event->getResponse()->headers->setCookie(new Cookie($session->getName(), $session->getId(), 0 === $params['lifetime'] ? 0 : time() + $params['lifetime'], $params['path'], $params['domain'], $params['secure'], $params['httponly'])); + } + } + + public static function getSubscribedEvents() + { + return array( + KernelEvents::REQUEST => array('onKernelRequest', 192), + KernelEvents::RESPONSE => array('onKernelResponse', -128), + ); + } + + /** + * Gets the session object. + * + * @return SessionInterface|null A SessionInterface instance or null if no session is available + */ + abstract protected function getSession(); +} diff --git a/lib/silex/vendor/symfony/http-kernel/EventListener/AddRequestFormatsListener.php b/lib/silex/vendor/symfony/http-kernel/EventListener/AddRequestFormatsListener.php new file mode 100644 index 000000000..f21fc6ab7 --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/EventListener/AddRequestFormatsListener.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\HttpKernel\EventListener; + +use Symfony\Component\EventDispatcher\EventSubscriberInterface; +use Symfony\Component\HttpKernel\KernelEvents; +use Symfony\Component\HttpKernel\Event\GetResponseEvent; + +/** + * Adds configured formats to each request. + * + * @author Gildas Quemener + */ +class AddRequestFormatsListener implements EventSubscriberInterface +{ + protected $formats; + + public function __construct(array $formats) + { + $this->formats = $formats; + } + + /** + * Adds request formats. + */ + public function onKernelRequest(GetResponseEvent $event) + { + $request = $event->getRequest(); + foreach ($this->formats as $format => $mimeTypes) { + $request->setFormat($format, $mimeTypes); + } + } + + /** + * {@inheritdoc} + */ + public static function getSubscribedEvents() + { + return array(KernelEvents::REQUEST => array('onKernelRequest', 1)); + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/EventListener/DebugHandlersListener.php b/lib/silex/vendor/symfony/http-kernel/EventListener/DebugHandlersListener.php new file mode 100644 index 000000000..2911caa11 --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/EventListener/DebugHandlersListener.php @@ -0,0 +1,156 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\EventListener; + +use Psr\Log\LoggerInterface; +use Symfony\Component\Debug\ErrorHandler; +use Symfony\Component\Debug\ExceptionHandler; +use Symfony\Component\EventDispatcher\Event; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; +use Symfony\Component\HttpKernel\Event\KernelEvent; +use Symfony\Component\HttpKernel\KernelEvents; +use Symfony\Component\Console\ConsoleEvents; +use Symfony\Component\Console\Event\ConsoleEvent; +use Symfony\Component\Console\Output\ConsoleOutputInterface; + +/** + * Configures errors and exceptions handlers. + * + * @author Nicolas Grekas + */ +class DebugHandlersListener implements EventSubscriberInterface +{ + private $exceptionHandler; + private $logger; + private $levels; + private $throwAt; + private $scream; + private $fileLinkFormat; + private $scope; + private $firstCall = true; + private $hasTerminatedWithException; + + /** + * @param callable|null $exceptionHandler A handler that will be called on Exception + * @param LoggerInterface|null $logger A PSR-3 logger + * @param array|int $levels An array map of E_* to LogLevel::* or an integer bit field of E_* constants + * @param int|null $throwAt Thrown errors in a bit field of E_* constants, or null to keep the current value + * @param bool $scream Enables/disables screaming mode, where even silenced errors are logged + * @param string|array $fileLinkFormat The format for links to source files + * @param bool $scope Enables/disables scoping mode + */ + public function __construct(callable $exceptionHandler = null, LoggerInterface $logger = null, $levels = E_ALL, $throwAt = E_ALL, $scream = true, $fileLinkFormat = null, $scope = true) + { + $this->exceptionHandler = $exceptionHandler; + $this->logger = $logger; + $this->levels = null === $levels ? E_ALL : $levels; + $this->throwAt = is_numeric($throwAt) ? (int) $throwAt : (null === $throwAt ? null : ($throwAt ? E_ALL : null)); + $this->scream = (bool) $scream; + $this->fileLinkFormat = $fileLinkFormat; + $this->scope = (bool) $scope; + } + + /** + * Configures the error handler. + */ + public function configure(Event $event = null) + { + if (!$event instanceof KernelEvent ? !$this->firstCall : !$event->isMasterRequest()) { + return; + } + $this->firstCall = $this->hasTerminatedWithException = false; + + $handler = set_exception_handler('var_dump'); + $handler = is_array($handler) ? $handler[0] : null; + restore_exception_handler(); + + if ($this->logger || null !== $this->throwAt) { + if ($handler instanceof ErrorHandler) { + if ($this->logger) { + $handler->setDefaultLogger($this->logger, $this->levels); + if (is_array($this->levels)) { + $levels = 0; + foreach ($this->levels as $type => $log) { + $levels |= $type; + } + } else { + $levels = $this->levels; + } + if ($this->scream) { + $handler->screamAt($levels); + } + if ($this->scope) { + $handler->scopeAt($levels & ~E_USER_DEPRECATED & ~E_DEPRECATED); + } else { + $handler->scopeAt(0, true); + } + $this->logger = $this->levels = null; + } + if (null !== $this->throwAt) { + $handler->throwAt($this->throwAt, true); + } + } + } + if (!$this->exceptionHandler) { + if ($event instanceof KernelEvent) { + if (method_exists($kernel = $event->getKernel(), 'terminateWithException')) { + $request = $event->getRequest(); + $hasRun = &$this->hasTerminatedWithException; + $this->exceptionHandler = function (\Exception $e) use ($kernel, $request, &$hasRun) { + if ($hasRun) { + throw $e; + } + $hasRun = true; + $kernel->terminateWithException($e, $request); + }; + } + } elseif ($event instanceof ConsoleEvent && $app = $event->getCommand()->getApplication()) { + $output = $event->getOutput(); + if ($output instanceof ConsoleOutputInterface) { + $output = $output->getErrorOutput(); + } + $this->exceptionHandler = function ($e) use ($app, $output) { + $app->renderException($e, $output); + }; + } + } + if ($this->exceptionHandler) { + if ($handler instanceof ErrorHandler) { + $h = $handler->setExceptionHandler('var_dump'); + if (is_array($h) && $h[0] instanceof ExceptionHandler) { + $handler->setExceptionHandler($h); + $handler = $h[0]; + } else { + $handler->setExceptionHandler($this->exceptionHandler); + } + } + if ($handler instanceof ExceptionHandler) { + $handler->setHandler($this->exceptionHandler); + if (null !== $this->fileLinkFormat) { + $handler->setFileLinkFormat($this->fileLinkFormat); + } + } + $this->exceptionHandler = null; + } + } + + public static function getSubscribedEvents() + { + $events = array(KernelEvents::REQUEST => array('configure', 2048)); + + if ('cli' === PHP_SAPI && defined('Symfony\Component\Console\ConsoleEvents::COMMAND')) { + $events[ConsoleEvents::COMMAND] = array('configure', 2048); + } + + return $events; + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/EventListener/DumpListener.php b/lib/silex/vendor/symfony/http-kernel/EventListener/DumpListener.php new file mode 100644 index 000000000..de19e1311 --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/EventListener/DumpListener.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\HttpKernel\EventListener; + +use Symfony\Component\Console\ConsoleEvents; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; +use Symfony\Component\VarDumper\Cloner\ClonerInterface; +use Symfony\Component\VarDumper\Dumper\DataDumperInterface; +use Symfony\Component\VarDumper\VarDumper; + +/** + * Configures dump() handler. + * + * @author Nicolas Grekas + */ +class DumpListener implements EventSubscriberInterface +{ + private $cloner; + private $dumper; + + public function __construct(ClonerInterface $cloner, DataDumperInterface $dumper) + { + $this->cloner = $cloner; + $this->dumper = $dumper; + } + + public function configure() + { + $cloner = $this->cloner; + $dumper = $this->dumper; + + VarDumper::setHandler(function ($var) use ($cloner, $dumper) { + $dumper->dump($cloner->cloneVar($var)); + }); + } + + public static function getSubscribedEvents() + { + if (!class_exists(ConsoleEvents::class)) { + return array(); + } + + // Register early to have a working dump() as early as possible + return array(ConsoleEvents::COMMAND => array('configure', 1024)); + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/EventListener/ExceptionListener.php b/lib/silex/vendor/symfony/http-kernel/EventListener/ExceptionListener.php new file mode 100644 index 000000000..f18e42c7d --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/EventListener/ExceptionListener.php @@ -0,0 +1,129 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\EventListener; + +use Psr\Log\LoggerInterface; +use Symfony\Component\Debug\Exception\FlattenException; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpKernel\Event\FilterResponseEvent; +use Symfony\Component\HttpKernel\Log\DebugLoggerInterface; +use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent; +use Symfony\Component\HttpKernel\KernelEvents; +use Symfony\Component\HttpKernel\HttpKernelInterface; +use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; + +/** + * ExceptionListener. + * + * @author Fabien Potencier + */ +class ExceptionListener implements EventSubscriberInterface +{ + protected $controller; + protected $logger; + protected $debug; + + public function __construct($controller, LoggerInterface $logger = null, $debug = false) + { + $this->controller = $controller; + $this->logger = $logger; + $this->debug = $debug; + } + + public function onKernelException(GetResponseForExceptionEvent $event) + { + $exception = $event->getException(); + $request = $event->getRequest(); + $eventDispatcher = func_num_args() > 2 ? func_get_arg(2) : null; + + $this->logException($exception, sprintf('Uncaught PHP Exception %s: "%s" at %s line %s', get_class($exception), $exception->getMessage(), $exception->getFile(), $exception->getLine())); + + $request = $this->duplicateRequest($exception, $request); + + try { + $response = $event->getKernel()->handle($request, HttpKernelInterface::SUB_REQUEST, false); + } catch (\Exception $e) { + $this->logException($e, sprintf('Exception thrown when handling an exception (%s: %s at %s line %s)', get_class($e), $e->getMessage(), $e->getFile(), $e->getLine())); + + $wrapper = $e; + + while ($prev = $wrapper->getPrevious()) { + if ($exception === $wrapper = $prev) { + throw $e; + } + } + + $prev = new \ReflectionProperty('Exception', 'previous'); + $prev->setAccessible(true); + $prev->setValue($wrapper, $exception); + + throw $e; + } + + $event->setResponse($response); + + if ($this->debug && $eventDispatcher instanceof EventDispatcherInterface) { + $cspRemovalListener = function (FilterResponseEvent $event) use (&$cspRemovalListener, $eventDispatcher) { + $event->getResponse()->headers->remove('Content-Security-Policy'); + $eventDispatcher->removeListener(KernelEvents::RESPONSE, $cspRemovalListener); + }; + $eventDispatcher->addListener(KernelEvents::RESPONSE, $cspRemovalListener, -128); + } + } + + public static function getSubscribedEvents() + { + return array( + KernelEvents::EXCEPTION => array('onKernelException', -128), + ); + } + + /** + * Logs an exception. + * + * @param \Exception $exception The \Exception instance + * @param string $message The error message to log + */ + protected function logException(\Exception $exception, $message) + { + if (null !== $this->logger) { + if (!$exception instanceof HttpExceptionInterface || $exception->getStatusCode() >= 500) { + $this->logger->critical($message, array('exception' => $exception)); + } else { + $this->logger->error($message, array('exception' => $exception)); + } + } + } + + /** + * Clones the request for the exception. + * + * @param \Exception $exception The thrown exception + * @param Request $request The original request + * + * @return Request $request The cloned request + */ + protected function duplicateRequest(\Exception $exception, Request $request) + { + $attributes = array( + '_controller' => $this->controller, + 'exception' => FlattenException::create($exception), + 'logger' => $this->logger instanceof DebugLoggerInterface ? $this->logger : null, + ); + $request = $request->duplicate(null, null, $attributes); + $request->setMethod('GET'); + + return $request; + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/EventListener/FragmentListener.php b/lib/silex/vendor/symfony/http-kernel/EventListener/FragmentListener.php new file mode 100644 index 000000000..da518fcc8 --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/EventListener/FragmentListener.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\HttpKernel\EventListener; + +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpKernel\Event\GetResponseEvent; +use Symfony\Component\HttpKernel\KernelEvents; +use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; +use Symfony\Component\HttpKernel\UriSigner; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; + +/** + * Handles content fragments represented by special URIs. + * + * All URL paths starting with /_fragment are handled as + * content fragments by this listener. + * + * If throws an AccessDeniedHttpException exception if the request + * is not signed or if it is not an internal sub-request. + * + * @author Fabien Potencier + */ +class FragmentListener implements EventSubscriberInterface +{ + private $signer; + private $fragmentPath; + + /** + * @param UriSigner $signer A UriSigner instance + * @param string $fragmentPath The path that triggers this listener + */ + public function __construct(UriSigner $signer, $fragmentPath = '/_fragment') + { + $this->signer = $signer; + $this->fragmentPath = $fragmentPath; + } + + /** + * Fixes request attributes when the path is '/_fragment'. + * + * @throws AccessDeniedHttpException if the request does not come from a trusted IP + */ + public function onKernelRequest(GetResponseEvent $event) + { + $request = $event->getRequest(); + + if ($this->fragmentPath !== rawurldecode($request->getPathInfo())) { + return; + } + + if ($request->attributes->has('_controller')) { + // Is a sub-request: no need to parse _path but it should still be removed from query parameters as below. + $request->query->remove('_path'); + + return; + } + + if ($event->isMasterRequest()) { + $this->validateRequest($request); + } + + parse_str($request->query->get('_path', ''), $attributes); + $request->attributes->add($attributes); + $request->attributes->set('_route_params', array_replace($request->attributes->get('_route_params', array()), $attributes)); + $request->query->remove('_path'); + } + + protected function validateRequest(Request $request) + { + // is the Request safe? + if (!$request->isMethodSafe(false)) { + throw new AccessDeniedHttpException(); + } + + // is the Request signed? + // we cannot use $request->getUri() here as we want to work with the original URI (no query string reordering) + if ($this->signer->check($request->getSchemeAndHttpHost().$request->getBaseUrl().$request->getPathInfo().(null !== ($qs = $request->server->get('QUERY_STRING')) ? '?'.$qs : ''))) { + return; + } + + throw new AccessDeniedHttpException(); + } + + public static function getSubscribedEvents() + { + return array( + KernelEvents::REQUEST => array(array('onKernelRequest', 48)), + ); + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/EventListener/LocaleListener.php b/lib/silex/vendor/symfony/http-kernel/EventListener/LocaleListener.php new file mode 100644 index 000000000..427ea82fc --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/EventListener/LocaleListener.php @@ -0,0 +1,83 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\EventListener; + +use Symfony\Component\HttpKernel\Event\GetResponseEvent; +use Symfony\Component\HttpKernel\Event\FinishRequestEvent; +use Symfony\Component\HttpKernel\KernelEvents; +use Symfony\Component\HttpFoundation\RequestStack; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\Routing\RequestContextAwareInterface; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; + +/** + * Initializes the locale based on the current request. + * + * @author Fabien Potencier + */ +class LocaleListener implements EventSubscriberInterface +{ + private $router; + private $defaultLocale; + private $requestStack; + + /** + * @param RequestStack $requestStack A RequestStack instance + * @param string $defaultLocale The default locale + * @param RequestContextAwareInterface|null $router The router + */ + public function __construct(RequestStack $requestStack, $defaultLocale = 'en', RequestContextAwareInterface $router = null) + { + $this->defaultLocale = $defaultLocale; + $this->requestStack = $requestStack; + $this->router = $router; + } + + public function onKernelRequest(GetResponseEvent $event) + { + $request = $event->getRequest(); + $request->setDefaultLocale($this->defaultLocale); + + $this->setLocale($request); + $this->setRouterContext($request); + } + + public function onKernelFinishRequest(FinishRequestEvent $event) + { + if (null !== $parentRequest = $this->requestStack->getParentRequest()) { + $this->setRouterContext($parentRequest); + } + } + + private function setLocale(Request $request) + { + if ($locale = $request->attributes->get('_locale')) { + $request->setLocale($locale); + } + } + + private function setRouterContext(Request $request) + { + if (null !== $this->router) { + $this->router->getContext()->setParameter('_locale', $request->getLocale()); + } + } + + public static function getSubscribedEvents() + { + return array( + // must be registered after the Router to have access to the _locale + KernelEvents::REQUEST => array(array('onKernelRequest', 16)), + KernelEvents::FINISH_REQUEST => array(array('onKernelFinishRequest', 0)), + ); + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/EventListener/ProfilerListener.php b/lib/silex/vendor/symfony/http-kernel/EventListener/ProfilerListener.php new file mode 100644 index 000000000..e3e4e7620 --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/EventListener/ProfilerListener.php @@ -0,0 +1,128 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\EventListener; + +use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent; +use Symfony\Component\HttpKernel\Event\FilterResponseEvent; +use Symfony\Component\HttpKernel\Event\PostResponseEvent; +use Symfony\Component\HttpKernel\KernelEvents; +use Symfony\Component\HttpKernel\Profiler\Profiler; +use Symfony\Component\HttpFoundation\RequestMatcherInterface; +use Symfony\Component\HttpFoundation\RequestStack; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; + +/** + * ProfilerListener collects data for the current request by listening to the kernel events. + * + * @author Fabien Potencier + */ +class ProfilerListener implements EventSubscriberInterface +{ + protected $profiler; + protected $matcher; + protected $onlyException; + protected $onlyMasterRequests; + protected $exception; + protected $profiles; + protected $requestStack; + protected $parents; + + /** + * @param Profiler $profiler A Profiler instance + * @param RequestStack $requestStack A RequestStack instance + * @param RequestMatcherInterface|null $matcher A RequestMatcher instance + * @param bool $onlyException True if the profiler only collects data when an exception occurs, false otherwise + * @param bool $onlyMasterRequests True if the profiler only collects data when the request is a master request, false otherwise + */ + public function __construct(Profiler $profiler, RequestStack $requestStack, RequestMatcherInterface $matcher = null, $onlyException = false, $onlyMasterRequests = false) + { + $this->profiler = $profiler; + $this->matcher = $matcher; + $this->onlyException = (bool) $onlyException; + $this->onlyMasterRequests = (bool) $onlyMasterRequests; + $this->profiles = new \SplObjectStorage(); + $this->parents = new \SplObjectStorage(); + $this->requestStack = $requestStack; + } + + /** + * Handles the onKernelException event. + */ + public function onKernelException(GetResponseForExceptionEvent $event) + { + if ($this->onlyMasterRequests && !$event->isMasterRequest()) { + return; + } + + $this->exception = $event->getException(); + } + + /** + * Handles the onKernelResponse event. + */ + public function onKernelResponse(FilterResponseEvent $event) + { + $master = $event->isMasterRequest(); + if ($this->onlyMasterRequests && !$master) { + return; + } + + if ($this->onlyException && null === $this->exception) { + return; + } + + $request = $event->getRequest(); + $exception = $this->exception; + $this->exception = null; + + if (null !== $this->matcher && !$this->matcher->matches($request)) { + return; + } + + if (!$profile = $this->profiler->collect($request, $event->getResponse(), $exception)) { + return; + } + + $this->profiles[$request] = $profile; + + $this->parents[$request] = $this->requestStack->getParentRequest(); + } + + public function onKernelTerminate(PostResponseEvent $event) + { + // attach children to parents + foreach ($this->profiles as $request) { + if (null !== $parentRequest = $this->parents[$request]) { + if (isset($this->profiles[$parentRequest])) { + $this->profiles[$parentRequest]->addChild($this->profiles[$request]); + } + } + } + + // save profiles + foreach ($this->profiles as $request) { + $this->profiler->saveProfile($this->profiles[$request]); + } + + $this->profiles = new \SplObjectStorage(); + $this->parents = new \SplObjectStorage(); + } + + public static function getSubscribedEvents() + { + return array( + KernelEvents::RESPONSE => array('onKernelResponse', -100), + KernelEvents::EXCEPTION => 'onKernelException', + KernelEvents::TERMINATE => array('onKernelTerminate', -1024), + ); + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/EventListener/ResponseListener.php b/lib/silex/vendor/symfony/http-kernel/EventListener/ResponseListener.php new file mode 100644 index 000000000..6d56197a7 --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/EventListener/ResponseListener.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\HttpKernel\EventListener; + +use Symfony\Component\HttpKernel\Event\FilterResponseEvent; +use Symfony\Component\HttpKernel\KernelEvents; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; + +/** + * ResponseListener fixes the Response headers based on the Request. + * + * @author Fabien Potencier + */ +class ResponseListener implements EventSubscriberInterface +{ + private $charset; + + public function __construct($charset) + { + $this->charset = $charset; + } + + /** + * Filters the Response. + */ + public function onKernelResponse(FilterResponseEvent $event) + { + if (!$event->isMasterRequest()) { + return; + } + + $response = $event->getResponse(); + + if (null === $response->getCharset()) { + $response->setCharset($this->charset); + } + + $response->prepare($event->getRequest()); + } + + public static function getSubscribedEvents() + { + return array( + KernelEvents::RESPONSE => 'onKernelResponse', + ); + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/EventListener/RouterListener.php b/lib/silex/vendor/symfony/http-kernel/EventListener/RouterListener.php new file mode 100644 index 000000000..caaf80f86 --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/EventListener/RouterListener.php @@ -0,0 +1,171 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\EventListener; + +use Psr\Log\LoggerInterface; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\Event\GetResponseEvent; +use Symfony\Component\HttpKernel\Event\FinishRequestEvent; +use Symfony\Component\HttpKernel\Kernel; +use Symfony\Component\HttpKernel\KernelEvents; +use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; +use Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException; +use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; +use Symfony\Component\HttpFoundation\RequestStack; +use Symfony\Component\Routing\Exception\MethodNotAllowedException; +use Symfony\Component\Routing\Exception\NoConfigurationException; +use Symfony\Component\Routing\Exception\ResourceNotFoundException; +use Symfony\Component\Routing\Matcher\UrlMatcherInterface; +use Symfony\Component\Routing\Matcher\RequestMatcherInterface; +use Symfony\Component\Routing\RequestContext; +use Symfony\Component\Routing\RequestContextAwareInterface; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; +use Symfony\Component\HttpFoundation\Request; + +/** + * Initializes the context from the request and sets request attributes based on a matching route. + * + * @author Fabien Potencier + * @author Yonel Ceruto + */ +class RouterListener implements EventSubscriberInterface +{ + private $matcher; + private $context; + private $logger; + private $requestStack; + private $projectDir; + private $debug; + + /** + * @param UrlMatcherInterface|RequestMatcherInterface $matcher The Url or Request matcher + * @param RequestStack $requestStack A RequestStack instance + * @param RequestContext|null $context The RequestContext (can be null when $matcher implements RequestContextAwareInterface) + * @param LoggerInterface|null $logger The logger + * @param string $projectDir + * @param bool $debug + * + * @throws \InvalidArgumentException + */ + public function __construct($matcher, RequestStack $requestStack, RequestContext $context = null, LoggerInterface $logger = null, $projectDir = null, $debug = true) + { + if (!$matcher instanceof UrlMatcherInterface && !$matcher instanceof RequestMatcherInterface) { + throw new \InvalidArgumentException('Matcher must either implement UrlMatcherInterface or RequestMatcherInterface.'); + } + + if (null === $context && !$matcher instanceof RequestContextAwareInterface) { + throw new \InvalidArgumentException('You must either pass a RequestContext or the matcher must implement RequestContextAwareInterface.'); + } + + $this->matcher = $matcher; + $this->context = $context ?: $matcher->getContext(); + $this->requestStack = $requestStack; + $this->logger = $logger; + $this->projectDir = $projectDir; + $this->debug = $debug; + } + + private function setCurrentRequest(Request $request = null) + { + if (null !== $request) { + try { + $this->context->fromRequest($request); + } catch (\UnexpectedValueException $e) { + throw new BadRequestHttpException($e->getMessage(), $e, $e->getCode()); + } + } + } + + /** + * After a sub-request is done, we need to reset the routing context to the parent request so that the URL generator + * operates on the correct context again. + * + * @param FinishRequestEvent $event + */ + public function onKernelFinishRequest(FinishRequestEvent $event) + { + $this->setCurrentRequest($this->requestStack->getParentRequest()); + } + + public function onKernelRequest(GetResponseEvent $event) + { + $request = $event->getRequest(); + + $this->setCurrentRequest($request); + + if ($request->attributes->has('_controller')) { + // routing is already done + return; + } + + // add attributes based on the request (routing) + try { + // matching a request is more powerful than matching a URL path + context, so try that first + if ($this->matcher instanceof RequestMatcherInterface) { + $parameters = $this->matcher->matchRequest($request); + } else { + $parameters = $this->matcher->match($request->getPathInfo()); + } + + if (null !== $this->logger) { + $this->logger->info('Matched route "{route}".', array( + 'route' => isset($parameters['_route']) ? $parameters['_route'] : 'n/a', + 'route_parameters' => $parameters, + 'request_uri' => $request->getUri(), + 'method' => $request->getMethod(), + )); + } + + $request->attributes->add($parameters); + unset($parameters['_route'], $parameters['_controller']); + $request->attributes->set('_route_params', $parameters); + } catch (ResourceNotFoundException $e) { + if ($this->debug && $e instanceof NoConfigurationException) { + $event->setResponse($this->createWelcomeResponse()); + + return; + } + + $message = sprintf('No route found for "%s %s"', $request->getMethod(), $request->getPathInfo()); + + if ($referer = $request->headers->get('referer')) { + $message .= sprintf(' (from "%s")', $referer); + } + + throw new NotFoundHttpException($message, $e); + } catch (MethodNotAllowedException $e) { + $message = sprintf('No route found for "%s %s": Method Not Allowed (Allow: %s)', $request->getMethod(), $request->getPathInfo(), implode(', ', $e->getAllowedMethods())); + + throw new MethodNotAllowedHttpException($e->getAllowedMethods(), $message, $e); + } + } + + public static function getSubscribedEvents() + { + return array( + KernelEvents::REQUEST => array(array('onKernelRequest', 32)), + KernelEvents::FINISH_REQUEST => array(array('onKernelFinishRequest', 0)), + ); + } + + private function createWelcomeResponse() + { + $version = Kernel::VERSION; + $baseDir = realpath($this->projectDir).DIRECTORY_SEPARATOR; + $docVersion = substr(Kernel::VERSION, 0, 3); + + ob_start(); + include __DIR__.'/../Resources/welcome.html.php'; + + return new Response(ob_get_clean(), Response::HTTP_NOT_FOUND); + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/EventListener/SaveSessionListener.php b/lib/silex/vendor/symfony/http-kernel/EventListener/SaveSessionListener.php new file mode 100644 index 000000000..36809b59a --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/EventListener/SaveSessionListener.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\HttpKernel\EventListener; + +use Symfony\Component\EventDispatcher\EventSubscriberInterface; +use Symfony\Component\HttpKernel\Event\FilterResponseEvent; +use Symfony\Component\HttpKernel\KernelEvents; + +/** + * Saves the session, in case it is still open, before sending the response/headers. + * + * This ensures several things in case the developer did not save the session explicitly: + * + * * If a session save handler without locking is used, it ensures the data is available + * on the next request, e.g. after a redirect. PHPs auto-save at script end via + * session_register_shutdown is executed after fastcgi_finish_request. So in this case + * the data could be missing the next request because it might not be saved the moment + * the new request is processed. + * * A locking save handler (e.g. the native 'files') circumvents concurrency problems like + * the one above. But by saving the session before long-running things in the terminate event, + * we ensure the session is not blocked longer than needed. + * * When regenerating the session ID no locking is involved in PHPs session design. See + * https://bugs.php.net/bug.php?id=61470 for a discussion. So in this case, the session must + * be saved anyway before sending the headers with the new session ID. Otherwise session + * data could get lost again for concurrent requests with the new ID. One result could be + * that you get logged out after just logging in. + * + * This listener should be executed as one of the last listeners, so that previous listeners + * can still operate on the open session. This prevents the overhead of restarting it. + * Listeners after closing the session can still work with the session as usual because + * Symfonys session implementation starts the session on demand. So writing to it after + * it is saved will just restart it. + * + * @author Tobias Schultze + */ +class SaveSessionListener implements EventSubscriberInterface +{ + public function onKernelResponse(FilterResponseEvent $event) + { + if (!$event->isMasterRequest()) { + return; + } + + $session = $event->getRequest()->getSession(); + if ($session && $session->isStarted()) { + $session->save(); + } + } + + public static function getSubscribedEvents() + { + return array( + // low priority but higher than StreamedResponseListener + KernelEvents::RESPONSE => array(array('onKernelResponse', -1000)), + ); + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/EventListener/SessionListener.php b/lib/silex/vendor/symfony/http-kernel/EventListener/SessionListener.php new file mode 100644 index 000000000..39ebfd922 --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/EventListener/SessionListener.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\HttpKernel\EventListener; + +use Psr\Container\ContainerInterface; + +/** + * Sets the session in the request. + * + * @author Fabien Potencier + * + * @final since version 3.3 + */ +class SessionListener extends AbstractSessionListener +{ + private $container; + + public function __construct(ContainerInterface $container) + { + $this->container = $container; + } + + protected function getSession() + { + if (!$this->container->has('session')) { + return; + } + + return $this->container->get('session'); + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/EventListener/StreamedResponseListener.php b/lib/silex/vendor/symfony/http-kernel/EventListener/StreamedResponseListener.php new file mode 100644 index 000000000..671db5d82 --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/EventListener/StreamedResponseListener.php @@ -0,0 +1,49 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\EventListener; + +use Symfony\Component\HttpFoundation\StreamedResponse; +use Symfony\Component\HttpKernel\Event\FilterResponseEvent; +use Symfony\Component\HttpKernel\KernelEvents; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; + +/** + * StreamedResponseListener is responsible for sending the Response + * to the client. + * + * @author Fabien Potencier + */ +class StreamedResponseListener implements EventSubscriberInterface +{ + /** + * Filters the Response. + */ + public function onKernelResponse(FilterResponseEvent $event) + { + if (!$event->isMasterRequest()) { + return; + } + + $response = $event->getResponse(); + + if ($response instanceof StreamedResponse) { + $response->send(); + } + } + + public static function getSubscribedEvents() + { + return array( + KernelEvents::RESPONSE => array('onKernelResponse', -1024), + ); + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/EventListener/SurrogateListener.php b/lib/silex/vendor/symfony/http-kernel/EventListener/SurrogateListener.php new file mode 100644 index 000000000..37d1b9d04 --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/EventListener/SurrogateListener.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\HttpKernel\EventListener; + +use Symfony\Component\HttpKernel\Event\FilterResponseEvent; +use Symfony\Component\HttpKernel\HttpCache\HttpCache; +use Symfony\Component\HttpKernel\HttpCache\SurrogateInterface; +use Symfony\Component\HttpKernel\KernelEvents; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; + +/** + * SurrogateListener adds a Surrogate-Control HTTP header when the Response needs to be parsed for Surrogates. + * + * @author Fabien Potencier + */ +class SurrogateListener implements EventSubscriberInterface +{ + private $surrogate; + + public function __construct(SurrogateInterface $surrogate = null) + { + $this->surrogate = $surrogate; + } + + /** + * Filters the Response. + */ + public function onKernelResponse(FilterResponseEvent $event) + { + if (!$event->isMasterRequest()) { + return; + } + + $kernel = $event->getKernel(); + $surrogate = $this->surrogate; + if ($kernel instanceof HttpCache) { + $surrogate = $kernel->getSurrogate(); + if (null !== $this->surrogate && $this->surrogate->getName() !== $surrogate->getName()) { + $surrogate = $this->surrogate; + } + } + + if (null === $surrogate) { + return; + } + + $surrogate->addSurrogateControl($event->getResponse()); + } + + public static function getSubscribedEvents() + { + return array( + KernelEvents::RESPONSE => 'onKernelResponse', + ); + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/EventListener/TestSessionListener.php b/lib/silex/vendor/symfony/http-kernel/EventListener/TestSessionListener.php new file mode 100644 index 000000000..36abb422f --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/EventListener/TestSessionListener.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\HttpKernel\EventListener; + +use Psr\Container\ContainerInterface; + +/** + * Sets the session in the request. + * + * @author Fabien Potencier + * + * @final since version 3.3 + */ +class TestSessionListener extends AbstractTestSessionListener +{ + private $container; + + public function __construct(ContainerInterface $container) + { + $this->container = $container; + } + + protected function getSession() + { + if (!$this->container->has('session')) { + return; + } + + return $this->container->get('session'); + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/EventListener/TranslatorListener.php b/lib/silex/vendor/symfony/http-kernel/EventListener/TranslatorListener.php new file mode 100644 index 000000000..6967ad029 --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/EventListener/TranslatorListener.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\HttpKernel\EventListener; + +use Symfony\Component\EventDispatcher\EventSubscriberInterface; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpKernel\Event\GetResponseEvent; +use Symfony\Component\HttpKernel\Event\FinishRequestEvent; +use Symfony\Component\HttpKernel\KernelEvents; +use Symfony\Component\HttpFoundation\RequestStack; +use Symfony\Component\Translation\TranslatorInterface; + +/** + * Synchronizes the locale between the request and the translator. + * + * @author Fabien Potencier + */ +class TranslatorListener implements EventSubscriberInterface +{ + private $translator; + private $requestStack; + + public function __construct(TranslatorInterface $translator, RequestStack $requestStack) + { + $this->translator = $translator; + $this->requestStack = $requestStack; + } + + public function onKernelRequest(GetResponseEvent $event) + { + $this->setLocale($event->getRequest()); + } + + public function onKernelFinishRequest(FinishRequestEvent $event) + { + if (null === $parentRequest = $this->requestStack->getParentRequest()) { + return; + } + + $this->setLocale($parentRequest); + } + + public static function getSubscribedEvents() + { + return array( + // must be registered after the Locale listener + KernelEvents::REQUEST => array(array('onKernelRequest', 10)), + KernelEvents::FINISH_REQUEST => array(array('onKernelFinishRequest', 0)), + ); + } + + private function setLocale(Request $request) + { + try { + $this->translator->setLocale($request->getLocale()); + } catch (\InvalidArgumentException $e) { + $this->translator->setLocale($request->getDefaultLocale()); + } + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/EventListener/ValidateRequestListener.php b/lib/silex/vendor/symfony/http-kernel/EventListener/ValidateRequestListener.php new file mode 100644 index 000000000..a33853f72 --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/EventListener/ValidateRequestListener.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\HttpKernel\EventListener; + +use Symfony\Component\EventDispatcher\EventSubscriberInterface; +use Symfony\Component\HttpKernel\Event\GetResponseEvent; +use Symfony\Component\HttpKernel\KernelEvents; + +/** + * Validates Requests. + * + * @author Magnus Nordlander + */ +class ValidateRequestListener implements EventSubscriberInterface +{ + /** + * Performs the validation. + */ + public function onKernelRequest(GetResponseEvent $event) + { + if (!$event->isMasterRequest()) { + return; + } + $request = $event->getRequest(); + + if ($request::getTrustedProxies()) { + $request->getClientIps(); + } + + $request->getHost(); + } + + /** + * {@inheritdoc} + */ + public static function getSubscribedEvents() + { + return array( + KernelEvents::REQUEST => array( + array('onKernelRequest', 256), + ), + ); + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/Exception/AccessDeniedHttpException.php b/lib/silex/vendor/symfony/http-kernel/Exception/AccessDeniedHttpException.php new file mode 100644 index 000000000..394188426 --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Exception/AccessDeniedHttpException.php @@ -0,0 +1,29 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Exception; + +/** + * @author Fabien Potencier + * @author Christophe Coevoet + */ +class AccessDeniedHttpException extends HttpException +{ + /** + * @param string $message The internal exception message + * @param \Exception $previous The previous exception + * @param int $code The internal exception code + */ + public function __construct($message = null, \Exception $previous = null, $code = 0) + { + parent::__construct(403, $message, $previous, array(), $code); + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/Exception/BadRequestHttpException.php b/lib/silex/vendor/symfony/http-kernel/Exception/BadRequestHttpException.php new file mode 100644 index 000000000..c28d83739 --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Exception/BadRequestHttpException.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\HttpKernel\Exception; + +/** + * @author Ben Ramsey + */ +class BadRequestHttpException extends HttpException +{ + /** + * @param string $message The internal exception message + * @param \Exception $previous The previous exception + * @param int $code The internal exception code + */ + public function __construct($message = null, \Exception $previous = null, $code = 0) + { + parent::__construct(400, $message, $previous, array(), $code); + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/Exception/ConflictHttpException.php b/lib/silex/vendor/symfony/http-kernel/Exception/ConflictHttpException.php new file mode 100644 index 000000000..79f24f2e5 --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Exception/ConflictHttpException.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\HttpKernel\Exception; + +/** + * @author Ben Ramsey + */ +class ConflictHttpException extends HttpException +{ + /** + * @param string $message The internal exception message + * @param \Exception $previous The previous exception + * @param int $code The internal exception code + */ + public function __construct($message = null, \Exception $previous = null, $code = 0) + { + parent::__construct(409, $message, $previous, array(), $code); + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/Exception/GoneHttpException.php b/lib/silex/vendor/symfony/http-kernel/Exception/GoneHttpException.php new file mode 100644 index 000000000..84e6915df --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Exception/GoneHttpException.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\HttpKernel\Exception; + +/** + * @author Ben Ramsey + */ +class GoneHttpException extends HttpException +{ + /** + * @param string $message The internal exception message + * @param \Exception $previous The previous exception + * @param int $code The internal exception code + */ + public function __construct($message = null, \Exception $previous = null, $code = 0) + { + parent::__construct(410, $message, $previous, array(), $code); + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/Exception/HttpException.php b/lib/silex/vendor/symfony/http-kernel/Exception/HttpException.php new file mode 100644 index 000000000..e8e376058 --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Exception/HttpException.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\HttpKernel\Exception; + +/** + * HttpException. + * + * @author Kris Wallsmith + */ +class HttpException extends \RuntimeException implements HttpExceptionInterface +{ + private $statusCode; + private $headers; + + public function __construct($statusCode, $message = null, \Exception $previous = null, array $headers = array(), $code = 0) + { + $this->statusCode = $statusCode; + $this->headers = $headers; + + parent::__construct($message, $code, $previous); + } + + public function getStatusCode() + { + return $this->statusCode; + } + + public function getHeaders() + { + return $this->headers; + } + + /** + * Set response headers. + * + * @param array $headers Response headers + */ + public function setHeaders(array $headers) + { + $this->headers = $headers; + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/Exception/HttpExceptionInterface.php b/lib/silex/vendor/symfony/http-kernel/Exception/HttpExceptionInterface.php new file mode 100644 index 000000000..8aa50a9f4 --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Exception/HttpExceptionInterface.php @@ -0,0 +1,34 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Exception; + +/** + * Interface for HTTP error exceptions. + * + * @author Kris Wallsmith + */ +interface HttpExceptionInterface +{ + /** + * Returns the status code. + * + * @return int An HTTP response status code + */ + public function getStatusCode(); + + /** + * Returns response headers. + * + * @return array Response headers + */ + public function getHeaders(); +} diff --git a/lib/silex/vendor/symfony/http-kernel/Exception/LengthRequiredHttpException.php b/lib/silex/vendor/symfony/http-kernel/Exception/LengthRequiredHttpException.php new file mode 100644 index 000000000..645efe87d --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Exception/LengthRequiredHttpException.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\HttpKernel\Exception; + +/** + * @author Ben Ramsey + */ +class LengthRequiredHttpException extends HttpException +{ + /** + * @param string $message The internal exception message + * @param \Exception $previous The previous exception + * @param int $code The internal exception code + */ + public function __construct($message = null, \Exception $previous = null, $code = 0) + { + parent::__construct(411, $message, $previous, array(), $code); + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/Exception/MethodNotAllowedHttpException.php b/lib/silex/vendor/symfony/http-kernel/Exception/MethodNotAllowedHttpException.php new file mode 100644 index 000000000..e308a5fee --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Exception/MethodNotAllowedHttpException.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\HttpKernel\Exception; + +/** + * @author Kris Wallsmith + */ +class MethodNotAllowedHttpException extends HttpException +{ + /** + * @param array $allow An array of allowed methods + * @param string $message The internal exception message + * @param \Exception $previous The previous exception + * @param int $code The internal exception code + */ + public function __construct(array $allow, $message = null, \Exception $previous = null, $code = 0) + { + $headers = array('Allow' => strtoupper(implode(', ', $allow))); + + parent::__construct(405, $message, $previous, $headers, $code); + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/Exception/NotAcceptableHttpException.php b/lib/silex/vendor/symfony/http-kernel/Exception/NotAcceptableHttpException.php new file mode 100644 index 000000000..097a13fd9 --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Exception/NotAcceptableHttpException.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\HttpKernel\Exception; + +/** + * @author Ben Ramsey + */ +class NotAcceptableHttpException extends HttpException +{ + /** + * @param string $message The internal exception message + * @param \Exception $previous The previous exception + * @param int $code The internal exception code + */ + public function __construct($message = null, \Exception $previous = null, $code = 0) + { + parent::__construct(406, $message, $previous, array(), $code); + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/Exception/NotFoundHttpException.php b/lib/silex/vendor/symfony/http-kernel/Exception/NotFoundHttpException.php new file mode 100644 index 000000000..878173cc7 --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Exception/NotFoundHttpException.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\HttpKernel\Exception; + +/** + * @author Fabien Potencier + */ +class NotFoundHttpException extends HttpException +{ + /** + * @param string $message The internal exception message + * @param \Exception $previous The previous exception + * @param int $code The internal exception code + */ + public function __construct($message = null, \Exception $previous = null, $code = 0) + { + parent::__construct(404, $message, $previous, array(), $code); + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/Exception/PreconditionFailedHttpException.php b/lib/silex/vendor/symfony/http-kernel/Exception/PreconditionFailedHttpException.php new file mode 100644 index 000000000..9f13a624c --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Exception/PreconditionFailedHttpException.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\HttpKernel\Exception; + +/** + * @author Ben Ramsey + */ +class PreconditionFailedHttpException extends HttpException +{ + /** + * @param string $message The internal exception message + * @param \Exception $previous The previous exception + * @param int $code The internal exception code + */ + public function __construct($message = null, \Exception $previous = null, $code = 0) + { + parent::__construct(412, $message, $previous, array(), $code); + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/Exception/PreconditionRequiredHttpException.php b/lib/silex/vendor/symfony/http-kernel/Exception/PreconditionRequiredHttpException.php new file mode 100644 index 000000000..9d0a36d7d --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Exception/PreconditionRequiredHttpException.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\HttpKernel\Exception; + +/** + * @author Ben Ramsey + * + * @see http://tools.ietf.org/html/rfc6585 + */ +class PreconditionRequiredHttpException extends HttpException +{ + /** + * @param string $message The internal exception message + * @param \Exception $previous The previous exception + * @param int $code The internal exception code + */ + public function __construct($message = null, \Exception $previous = null, $code = 0) + { + parent::__construct(428, $message, $previous, array(), $code); + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/Exception/ServiceUnavailableHttpException.php b/lib/silex/vendor/symfony/http-kernel/Exception/ServiceUnavailableHttpException.php new file mode 100644 index 000000000..b2767c3fd --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Exception/ServiceUnavailableHttpException.php @@ -0,0 +1,34 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Exception; + +/** + * @author Ben Ramsey + */ +class ServiceUnavailableHttpException extends HttpException +{ + /** + * @param int|string $retryAfter The number of seconds or HTTP-date after which the request may be retried + * @param string $message The internal exception message + * @param \Exception $previous The previous exception + * @param int $code The internal exception code + */ + public function __construct($retryAfter = null, $message = null, \Exception $previous = null, $code = 0) + { + $headers = array(); + if ($retryAfter) { + $headers = array('Retry-After' => $retryAfter); + } + + parent::__construct(503, $message, $previous, $headers, $code); + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/Exception/TooManyRequestsHttpException.php b/lib/silex/vendor/symfony/http-kernel/Exception/TooManyRequestsHttpException.php new file mode 100644 index 000000000..7d8a80332 --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Exception/TooManyRequestsHttpException.php @@ -0,0 +1,36 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Exception; + +/** + * @author Ben Ramsey + * + * @see http://tools.ietf.org/html/rfc6585 + */ +class TooManyRequestsHttpException extends HttpException +{ + /** + * @param int|string $retryAfter The number of seconds or HTTP-date after which the request may be retried + * @param string $message The internal exception message + * @param \Exception $previous The previous exception + * @param int $code The internal exception code + */ + public function __construct($retryAfter = null, $message = null, \Exception $previous = null, $code = 0) + { + $headers = array(); + if ($retryAfter) { + $headers = array('Retry-After' => $retryAfter); + } + + parent::__construct(429, $message, $previous, $headers, $code); + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/Exception/UnauthorizedHttpException.php b/lib/silex/vendor/symfony/http-kernel/Exception/UnauthorizedHttpException.php new file mode 100644 index 000000000..05ac875c5 --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Exception/UnauthorizedHttpException.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\HttpKernel\Exception; + +/** + * @author Ben Ramsey + */ +class UnauthorizedHttpException extends HttpException +{ + /** + * @param string $challenge WWW-Authenticate challenge string + * @param string $message The internal exception message + * @param \Exception $previous The previous exception + * @param int $code The internal exception code + */ + public function __construct($challenge, $message = null, \Exception $previous = null, $code = 0) + { + $headers = array('WWW-Authenticate' => $challenge); + + parent::__construct(401, $message, $previous, $headers, $code); + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/Exception/UnprocessableEntityHttpException.php b/lib/silex/vendor/symfony/http-kernel/Exception/UnprocessableEntityHttpException.php new file mode 100644 index 000000000..01b8b8465 --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Exception/UnprocessableEntityHttpException.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\HttpKernel\Exception; + +/** + * @author Steve Hutchins + */ +class UnprocessableEntityHttpException extends HttpException +{ + /** + * @param string $message The internal exception message + * @param \Exception $previous The previous exception + * @param int $code The internal exception code + */ + public function __construct($message = null, \Exception $previous = null, $code = 0) + { + parent::__construct(422, $message, $previous, array(), $code); + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/Exception/UnsupportedMediaTypeHttpException.php b/lib/silex/vendor/symfony/http-kernel/Exception/UnsupportedMediaTypeHttpException.php new file mode 100644 index 000000000..6913504e8 --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Exception/UnsupportedMediaTypeHttpException.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\HttpKernel\Exception; + +/** + * @author Ben Ramsey + */ +class UnsupportedMediaTypeHttpException extends HttpException +{ + /** + * @param string $message The internal exception message + * @param \Exception $previous The previous exception + * @param int $code The internal exception code + */ + public function __construct($message = null, \Exception $previous = null, $code = 0) + { + parent::__construct(415, $message, $previous, array(), $code); + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/Fragment/AbstractSurrogateFragmentRenderer.php b/lib/silex/vendor/symfony/http-kernel/Fragment/AbstractSurrogateFragmentRenderer.php new file mode 100644 index 000000000..a8dca41ba --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Fragment/AbstractSurrogateFragmentRenderer.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\HttpKernel\Fragment; + +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\Controller\ControllerReference; +use Symfony\Component\HttpKernel\HttpCache\SurrogateInterface; +use Symfony\Component\HttpKernel\UriSigner; + +/** + * Implements Surrogate rendering strategy. + * + * @author Fabien Potencier + */ +abstract class AbstractSurrogateFragmentRenderer extends RoutableFragmentRenderer +{ + private $surrogate; + private $inlineStrategy; + private $signer; + + /** + * The "fallback" strategy when surrogate is not available should always be an + * instance of InlineFragmentRenderer. + * + * @param SurrogateInterface $surrogate An Surrogate instance + * @param FragmentRendererInterface $inlineStrategy The inline strategy to use when the surrogate is not supported + * @param UriSigner $signer + */ + public function __construct(SurrogateInterface $surrogate = null, FragmentRendererInterface $inlineStrategy, UriSigner $signer = null) + { + $this->surrogate = $surrogate; + $this->inlineStrategy = $inlineStrategy; + $this->signer = $signer; + } + + /** + * {@inheritdoc} + * + * Note that if the current Request has no surrogate capability, this method + * falls back to use the inline rendering strategy. + * + * Additional available options: + * + * * alt: an alternative URI to render in case of an error + * * comment: a comment to add when returning the surrogate tag + * + * Note, that not all surrogate strategies support all options. For now + * 'alt' and 'comment' are only supported by ESI. + * + * @see Symfony\Component\HttpKernel\HttpCache\SurrogateInterface + */ + public function render($uri, Request $request, array $options = array()) + { + if (!$this->surrogate || !$this->surrogate->hasSurrogateCapability($request)) { + if ($uri instanceof ControllerReference && $this->containsNonScalars($uri->attributes)) { + @trigger_error('Passing non-scalar values as part of URI attributes to the ESI and SSI rendering strategies is deprecated since Symfony 3.1, and will be removed in 4.0. Use a different rendering strategy or pass scalar values.', E_USER_DEPRECATED); + } + + return $this->inlineStrategy->render($uri, $request, $options); + } + + if ($uri instanceof ControllerReference) { + $uri = $this->generateSignedFragmentUri($uri, $request); + } + + $alt = isset($options['alt']) ? $options['alt'] : null; + if ($alt instanceof ControllerReference) { + $alt = $this->generateSignedFragmentUri($alt, $request); + } + + $tag = $this->surrogate->renderIncludeTag($uri, $alt, isset($options['ignore_errors']) ? $options['ignore_errors'] : false, isset($options['comment']) ? $options['comment'] : ''); + + return new Response($tag); + } + + private function generateSignedFragmentUri($uri, Request $request) + { + if (null === $this->signer) { + throw new \LogicException('You must use a URI when using the ESI rendering strategy or set a URL signer.'); + } + + // we need to sign the absolute URI, but want to return the path only. + $fragmentUri = $this->signer->sign($this->generateFragmentUri($uri, $request, true)); + + return substr($fragmentUri, strlen($request->getSchemeAndHttpHost())); + } + + private function containsNonScalars(array $values) + { + foreach ($values as $value) { + if (is_array($value)) { + return $this->containsNonScalars($value); + } elseif (!is_scalar($value) && null !== $value) { + return true; + } + } + + return false; + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/Fragment/EsiFragmentRenderer.php b/lib/silex/vendor/symfony/http-kernel/Fragment/EsiFragmentRenderer.php new file mode 100644 index 000000000..a4570e3be --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Fragment/EsiFragmentRenderer.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\HttpKernel\Fragment; + +/** + * Implements the ESI rendering strategy. + * + * @author Fabien Potencier + */ +class EsiFragmentRenderer extends AbstractSurrogateFragmentRenderer +{ + /** + * {@inheritdoc} + */ + public function getName() + { + return 'esi'; + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/Fragment/FragmentHandler.php b/lib/silex/vendor/symfony/http-kernel/Fragment/FragmentHandler.php new file mode 100644 index 000000000..c9d952ffb --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Fragment/FragmentHandler.php @@ -0,0 +1,112 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Fragment; + +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpFoundation\StreamedResponse; +use Symfony\Component\HttpFoundation\RequestStack; +use Symfony\Component\HttpKernel\Controller\ControllerReference; + +/** + * Renders a URI that represents a resource fragment. + * + * This class handles the rendering of resource fragments that are included into + * a main resource. The handling of the rendering is managed by specialized renderers. + * + * @author Fabien Potencier + * + * @see FragmentRendererInterface + */ +class FragmentHandler +{ + private $debug; + private $renderers = array(); + private $requestStack; + + /** + * @param RequestStack $requestStack The Request stack that controls the lifecycle of requests + * @param FragmentRendererInterface[] $renderers An array of FragmentRendererInterface instances + * @param bool $debug Whether the debug mode is enabled or not + */ + public function __construct(RequestStack $requestStack, array $renderers = array(), $debug = false) + { + $this->requestStack = $requestStack; + foreach ($renderers as $renderer) { + $this->addRenderer($renderer); + } + $this->debug = $debug; + } + + /** + * Adds a renderer. + */ + public function addRenderer(FragmentRendererInterface $renderer) + { + $this->renderers[$renderer->getName()] = $renderer; + } + + /** + * Renders a URI and returns the Response content. + * + * Available options: + * + * * ignore_errors: true to return an empty string in case of an error + * + * @param string|ControllerReference $uri A URI as a string or a ControllerReference instance + * @param string $renderer The renderer name + * @param array $options An array of options + * + * @return string|null The Response content or null when the Response is streamed + * + * @throws \InvalidArgumentException when the renderer does not exist + * @throws \LogicException when no master request is being handled + */ + public function render($uri, $renderer = 'inline', array $options = array()) + { + if (!isset($options['ignore_errors'])) { + $options['ignore_errors'] = !$this->debug; + } + + if (!isset($this->renderers[$renderer])) { + throw new \InvalidArgumentException(sprintf('The "%s" renderer does not exist.', $renderer)); + } + + if (!$request = $this->requestStack->getCurrentRequest()) { + throw new \LogicException('Rendering a fragment can only be done when handling a Request.'); + } + + return $this->deliver($this->renderers[$renderer]->render($uri, $request, $options)); + } + + /** + * Delivers the Response as a string. + * + * When the Response is a StreamedResponse, the content is streamed immediately + * instead of being returned. + * + * @return string|null The Response content or null when the Response is streamed + * + * @throws \RuntimeException when the Response is not successful + */ + protected function deliver(Response $response) + { + if (!$response->isSuccessful()) { + throw new \RuntimeException(sprintf('Error when rendering "%s" (Status code is %s).', $this->requestStack->getCurrentRequest()->getUri(), $response->getStatusCode())); + } + + if (!$response instanceof StreamedResponse) { + return $response->getContent(); + } + + $response->sendContent(); + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/Fragment/FragmentRendererInterface.php b/lib/silex/vendor/symfony/http-kernel/Fragment/FragmentRendererInterface.php new file mode 100644 index 000000000..b177c3ac1 --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Fragment/FragmentRendererInterface.php @@ -0,0 +1,42 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Fragment; + +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpKernel\Controller\ControllerReference; +use Symfony\Component\HttpFoundation\Response; + +/** + * Interface implemented by all rendering strategies. + * + * @author Fabien Potencier + */ +interface FragmentRendererInterface +{ + /** + * Renders a URI and returns the Response content. + * + * @param string|ControllerReference $uri A URI as a string or a ControllerReference instance + * @param Request $request A Request instance + * @param array $options An array of options + * + * @return Response A Response instance + */ + public function render($uri, Request $request, array $options = array()); + + /** + * Gets the name of the strategy. + * + * @return string The strategy name + */ + public function getName(); +} diff --git a/lib/silex/vendor/symfony/http-kernel/Fragment/HIncludeFragmentRenderer.php b/lib/silex/vendor/symfony/http-kernel/Fragment/HIncludeFragmentRenderer.php new file mode 100644 index 000000000..7e957d4c9 --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Fragment/HIncludeFragmentRenderer.php @@ -0,0 +1,165 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Fragment; + +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\Templating\EngineInterface; +use Symfony\Component\HttpKernel\Controller\ControllerReference; +use Symfony\Component\HttpKernel\UriSigner; +use Twig\Environment; +use Twig\Error\LoaderError; +use Twig\Loader\ExistsLoaderInterface; + +/** + * Implements the Hinclude rendering strategy. + * + * @author Fabien Potencier + */ +class HIncludeFragmentRenderer extends RoutableFragmentRenderer +{ + private $globalDefaultTemplate; + private $signer; + private $templating; + private $charset; + + /** + * @param EngineInterface|Environment $templating An EngineInterface or a Twig instance + * @param UriSigner $signer A UriSigner instance + * @param string $globalDefaultTemplate The global default content (it can be a template name or the content) + * @param string $charset + */ + public function __construct($templating = null, UriSigner $signer = null, $globalDefaultTemplate = null, $charset = 'utf-8') + { + $this->setTemplating($templating); + $this->globalDefaultTemplate = $globalDefaultTemplate; + $this->signer = $signer; + $this->charset = $charset; + } + + /** + * Sets the templating engine to use to render the default content. + * + * @param EngineInterface|Environment|null $templating An EngineInterface or an Environment instance + * + * @throws \InvalidArgumentException + */ + public function setTemplating($templating) + { + if (null !== $templating && !$templating instanceof EngineInterface && !$templating instanceof Environment) { + throw new \InvalidArgumentException('The hinclude rendering strategy needs an instance of Twig\Environment or Symfony\Component\Templating\EngineInterface'); + } + + $this->templating = $templating; + } + + /** + * Checks if a templating engine has been set. + * + * @return bool true if the templating engine has been set, false otherwise + */ + public function hasTemplating() + { + return null !== $this->templating; + } + + /** + * {@inheritdoc} + * + * Additional available options: + * + * * default: The default content (it can be a template name or the content) + * * id: An optional hx:include tag id attribute + * * attributes: An optional array of hx:include tag attributes + */ + public function render($uri, Request $request, array $options = array()) + { + if ($uri instanceof ControllerReference) { + if (null === $this->signer) { + throw new \LogicException('You must use a proper URI when using the Hinclude rendering strategy or set a URL signer.'); + } + + // we need to sign the absolute URI, but want to return the path only. + $uri = substr($this->signer->sign($this->generateFragmentUri($uri, $request, true)), strlen($request->getSchemeAndHttpHost())); + } + + // We need to replace ampersands in the URI with the encoded form in order to return valid html/xml content. + $uri = str_replace('&', '&', $uri); + + $template = isset($options['default']) ? $options['default'] : $this->globalDefaultTemplate; + if (null !== $this->templating && $template && $this->templateExists($template)) { + $content = $this->templating->render($template); + } else { + $content = $template; + } + + $attributes = isset($options['attributes']) && is_array($options['attributes']) ? $options['attributes'] : array(); + if (isset($options['id']) && $options['id']) { + $attributes['id'] = $options['id']; + } + $renderedAttributes = ''; + if (count($attributes) > 0) { + $flags = ENT_QUOTES | ENT_SUBSTITUTE; + foreach ($attributes as $attribute => $value) { + $renderedAttributes .= sprintf( + ' %s="%s"', + htmlspecialchars($attribute, $flags, $this->charset, false), + htmlspecialchars($value, $flags, $this->charset, false) + ); + } + } + + return new Response(sprintf('%s', $uri, $renderedAttributes, $content)); + } + + /** + * @param string $template + * + * @return bool + */ + private function templateExists($template) + { + if ($this->templating instanceof EngineInterface) { + try { + return $this->templating->exists($template); + } catch (\InvalidArgumentException $e) { + return false; + } + } + + $loader = $this->templating->getLoader(); + if ($loader instanceof ExistsLoaderInterface || method_exists($loader, 'exists')) { + return $loader->exists($template); + } + + try { + if (method_exists($loader, 'getSourceContext')) { + $loader->getSourceContext($template); + } else { + $loader->getSource($template); + } + + return true; + } catch (LoaderError $e) { + } + + return false; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'hinclude'; + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/Fragment/InlineFragmentRenderer.php b/lib/silex/vendor/symfony/http-kernel/Fragment/InlineFragmentRenderer.php new file mode 100644 index 000000000..3f3a51a59 --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Fragment/InlineFragmentRenderer.php @@ -0,0 +1,152 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Fragment; + +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\HttpKernelInterface; +use Symfony\Component\HttpKernel\Controller\ControllerReference; +use Symfony\Component\HttpKernel\KernelEvents; +use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; + +/** + * Implements the inline rendering strategy where the Request is rendered by the current HTTP kernel. + * + * @author Fabien Potencier + */ +class InlineFragmentRenderer extends RoutableFragmentRenderer +{ + private $kernel; + private $dispatcher; + + public function __construct(HttpKernelInterface $kernel, EventDispatcherInterface $dispatcher = null) + { + $this->kernel = $kernel; + $this->dispatcher = $dispatcher; + } + + /** + * {@inheritdoc} + * + * Additional available options: + * + * * alt: an alternative URI to render in case of an error + */ + public function render($uri, Request $request, array $options = array()) + { + $reference = null; + if ($uri instanceof ControllerReference) { + $reference = $uri; + + // Remove attributes from the generated URI because if not, the Symfony + // routing system will use them to populate the Request attributes. We don't + // want that as we want to preserve objects (so we manually set Request attributes + // below instead) + $attributes = $reference->attributes; + $reference->attributes = array(); + + // The request format and locale might have been overridden by the user + foreach (array('_format', '_locale') as $key) { + if (isset($attributes[$key])) { + $reference->attributes[$key] = $attributes[$key]; + } + } + + $uri = $this->generateFragmentUri($uri, $request, false, false); + + $reference->attributes = array_merge($attributes, $reference->attributes); + } + + $subRequest = $this->createSubRequest($uri, $request); + + // override Request attributes as they can be objects (which are not supported by the generated URI) + if (null !== $reference) { + $subRequest->attributes->add($reference->attributes); + } + + $level = ob_get_level(); + try { + return $this->kernel->handle($subRequest, HttpKernelInterface::SUB_REQUEST, false); + } catch (\Exception $e) { + // we dispatch the exception event to trigger the logging + // the response that comes back is simply ignored + if (isset($options['ignore_errors']) && $options['ignore_errors'] && $this->dispatcher) { + $event = new GetResponseForExceptionEvent($this->kernel, $request, HttpKernelInterface::SUB_REQUEST, $e); + + $this->dispatcher->dispatch(KernelEvents::EXCEPTION, $event); + } + + // let's clean up the output buffers that were created by the sub-request + Response::closeOutputBuffers($level, false); + + if (isset($options['alt'])) { + $alt = $options['alt']; + unset($options['alt']); + + return $this->render($alt, $request, $options); + } + + if (!isset($options['ignore_errors']) || !$options['ignore_errors']) { + throw $e; + } + + return new Response(); + } + } + + protected function createSubRequest($uri, Request $request) + { + $cookies = $request->cookies->all(); + $server = $request->server->all(); + + // Override the arguments to emulate a sub-request. + // Sub-request object will point to localhost as client ip and real client ip + // will be included into trusted header for client ip + try { + if (Request::HEADER_X_FORWARDED_FOR & Request::getTrustedHeaderSet()) { + $currentXForwardedFor = $request->headers->get('X_FORWARDED_FOR', ''); + + $server['HTTP_X_FORWARDED_FOR'] = ($currentXForwardedFor ? $currentXForwardedFor.', ' : '').$request->getClientIp(); + } elseif (method_exists(Request::class, 'getTrustedHeaderName') && $trustedHeaderName = Request::getTrustedHeaderName(Request::HEADER_CLIENT_IP, false)) { + $currentXForwardedFor = $request->headers->get($trustedHeaderName, ''); + + $server['HTTP_'.$trustedHeaderName] = ($currentXForwardedFor ? $currentXForwardedFor.', ' : '').$request->getClientIp(); + } + } catch (\InvalidArgumentException $e) { + // Do nothing + } + + $server['REMOTE_ADDR'] = '127.0.0.1'; + unset($server['HTTP_IF_MODIFIED_SINCE']); + unset($server['HTTP_IF_NONE_MATCH']); + + $subRequest = Request::create($uri, 'get', array(), $cookies, array(), $server); + if ($request->headers->has('Surrogate-Capability')) { + $subRequest->headers->set('Surrogate-Capability', $request->headers->get('Surrogate-Capability')); + } + + if ($session = $request->getSession()) { + $subRequest->setSession($session); + } + + return $subRequest; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'inline'; + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/Fragment/RoutableFragmentRenderer.php b/lib/silex/vendor/symfony/http-kernel/Fragment/RoutableFragmentRenderer.php new file mode 100644 index 000000000..d7eeb89a6 --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Fragment/RoutableFragmentRenderer.php @@ -0,0 +1,90 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Fragment; + +use Symfony\Component\HttpKernel\Controller\ControllerReference; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpKernel\EventListener\FragmentListener; + +/** + * Adds the possibility to generate a fragment URI for a given Controller. + * + * @author Fabien Potencier + */ +abstract class RoutableFragmentRenderer implements FragmentRendererInterface +{ + private $fragmentPath = '/_fragment'; + + /** + * Sets the fragment path that triggers the fragment listener. + * + * @param string $path The path + * + * @see FragmentListener + */ + public function setFragmentPath($path) + { + $this->fragmentPath = $path; + } + + /** + * Generates a fragment URI for a given controller. + * + * @param ControllerReference $reference A ControllerReference instance + * @param Request $request A Request instance + * @param bool $absolute Whether to generate an absolute URL or not + * @param bool $strict Whether to allow non-scalar attributes or not + * + * @return string A fragment URI + */ + protected function generateFragmentUri(ControllerReference $reference, Request $request, $absolute = false, $strict = true) + { + if ($strict) { + $this->checkNonScalar($reference->attributes); + } + + // We need to forward the current _format and _locale values as we don't have + // a proper routing pattern to do the job for us. + // This makes things inconsistent if you switch from rendering a controller + // to rendering a route if the route pattern does not contain the special + // _format and _locale placeholders. + if (!isset($reference->attributes['_format'])) { + $reference->attributes['_format'] = $request->getRequestFormat(); + } + if (!isset($reference->attributes['_locale'])) { + $reference->attributes['_locale'] = $request->getLocale(); + } + + $reference->attributes['_controller'] = $reference->controller; + + $reference->query['_path'] = http_build_query($reference->attributes, '', '&'); + + $path = $this->fragmentPath.'?'.http_build_query($reference->query, '', '&'); + + if ($absolute) { + return $request->getUriForPath($path); + } + + return $request->getBaseUrl().$path; + } + + private function checkNonScalar($values) + { + foreach ($values as $key => $value) { + if (is_array($value)) { + $this->checkNonScalar($value); + } elseif (!is_scalar($value) && null !== $value) { + throw new \LogicException(sprintf('Controller attributes cannot contain non-scalar/non-null values (value for key "%s" is not a scalar or null).', $key)); + } + } + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/Fragment/SsiFragmentRenderer.php b/lib/silex/vendor/symfony/http-kernel/Fragment/SsiFragmentRenderer.php new file mode 100644 index 000000000..45e7122f0 --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Fragment/SsiFragmentRenderer.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\HttpKernel\Fragment; + +/** + * Implements the SSI rendering strategy. + * + * @author Sebastian Krebs + */ +class SsiFragmentRenderer extends AbstractSurrogateFragmentRenderer +{ + /** + * {@inheritdoc} + */ + public function getName() + { + return 'ssi'; + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/HttpCache/AbstractSurrogate.php b/lib/silex/vendor/symfony/http-kernel/HttpCache/AbstractSurrogate.php new file mode 100644 index 000000000..3b2d5f262 --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/HttpCache/AbstractSurrogate.php @@ -0,0 +1,134 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\HttpCache; + +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\HttpKernelInterface; + +/** + * Abstract class implementing Surrogate capabilities to Request and Response instances. + * + * @author Fabien Potencier + * @author Robin Chalas + */ +abstract class AbstractSurrogate implements SurrogateInterface +{ + protected $contentTypes; + protected $phpEscapeMap = array( + array('', '', '', ''), + ); + + /** + * @param array $contentTypes An array of content-type that should be parsed for Surrogate information + * (default: text/html, text/xml, application/xhtml+xml, and application/xml) + */ + public function __construct(array $contentTypes = array('text/html', 'text/xml', 'application/xhtml+xml', 'application/xml')) + { + $this->contentTypes = $contentTypes; + } + + /** + * Returns a new cache strategy instance. + * + * @return ResponseCacheStrategyInterface A ResponseCacheStrategyInterface instance + */ + public function createCacheStrategy() + { + return new ResponseCacheStrategy(); + } + + /** + * {@inheritdoc} + */ + public function hasSurrogateCapability(Request $request) + { + if (null === $value = $request->headers->get('Surrogate-Capability')) { + return false; + } + + return false !== strpos($value, sprintf('%s/1.0', strtoupper($this->getName()))); + } + + /** + * {@inheritdoc} + */ + public function addSurrogateCapability(Request $request) + { + $current = $request->headers->get('Surrogate-Capability'); + $new = sprintf('symfony="%s/1.0"', strtoupper($this->getName())); + + $request->headers->set('Surrogate-Capability', $current ? $current.', '.$new : $new); + } + + /** + * {@inheritdoc} + */ + public function needsParsing(Response $response) + { + if (!$control = $response->headers->get('Surrogate-Control')) { + return false; + } + + $pattern = sprintf('#content="[^"]*%s/1.0[^"]*"#', strtoupper($this->getName())); + + return (bool) preg_match($pattern, $control); + } + + /** + * {@inheritdoc} + */ + public function handle(HttpCache $cache, $uri, $alt, $ignoreErrors) + { + $subRequest = Request::create($uri, Request::METHOD_GET, array(), $cache->getRequest()->cookies->all(), array(), $cache->getRequest()->server->all()); + + try { + $response = $cache->handle($subRequest, HttpKernelInterface::SUB_REQUEST, true); + + if (!$response->isSuccessful()) { + throw new \RuntimeException(sprintf('Error when rendering "%s" (Status code is %s).', $subRequest->getUri(), $response->getStatusCode())); + } + + return $response->getContent(); + } catch (\Exception $e) { + if ($alt) { + return $this->handle($cache, $alt, '', $ignoreErrors); + } + + if (!$ignoreErrors) { + throw $e; + } + } + } + + /** + * Remove the Surrogate from the Surrogate-Control header. + */ + protected function removeFromControl(Response $response) + { + if (!$response->headers->has('Surrogate-Control')) { + return; + } + + $value = $response->headers->get('Surrogate-Control'); + $upperName = strtoupper($this->getName()); + + if (sprintf('content="%s/1.0"', $upperName) == $value) { + $response->headers->remove('Surrogate-Control'); + } elseif (preg_match(sprintf('#,\s*content="%s/1.0"#', $upperName), $value)) { + $response->headers->set('Surrogate-Control', preg_replace(sprintf('#,\s*content="%s/1.0"#', $upperName), '', $value)); + } elseif (preg_match(sprintf('#content="%s/1.0",\s*#', $upperName), $value)) { + $response->headers->set('Surrogate-Control', preg_replace(sprintf('#content="%s/1.0",\s*#', $upperName), '', $value)); + } + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/HttpCache/Esi.php b/lib/silex/vendor/symfony/http-kernel/HttpCache/Esi.php new file mode 100644 index 000000000..d09907ea6 --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/HttpCache/Esi.php @@ -0,0 +1,115 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\HttpCache; + +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +/** + * Esi implements the ESI capabilities to Request and Response instances. + * + * For more information, read the following W3C notes: + * + * * ESI Language Specification 1.0 (http://www.w3.org/TR/esi-lang) + * + * * Edge Architecture Specification (http://www.w3.org/TR/edge-arch) + * + * @author Fabien Potencier + */ +class Esi extends AbstractSurrogate +{ + public function getName() + { + return 'esi'; + } + + /** + * {@inheritdoc} + */ + public function addSurrogateControl(Response $response) + { + if (false !== strpos($response->getContent(), 'headers->set('Surrogate-Control', 'content="ESI/1.0"'); + } + } + + /** + * {@inheritdoc} + */ + public function renderIncludeTag($uri, $alt = null, $ignoreErrors = true, $comment = '') + { + $html = sprintf('', + $uri, + $ignoreErrors ? ' onerror="continue"' : '', + $alt ? sprintf(' alt="%s"', $alt) : '' + ); + + if (!empty($comment)) { + return sprintf("\n%s", $comment, $html); + } + + return $html; + } + + /** + * {@inheritdoc} + */ + public function process(Request $request, Response $response) + { + $type = $response->headers->get('Content-Type'); + if (empty($type)) { + $type = 'text/html'; + } + + $parts = explode(';', $type); + if (!in_array($parts[0], $this->contentTypes)) { + return $response; + } + + // we don't use a proper XML parser here as we can have ESI tags in a plain text response + $content = $response->getContent(); + $content = preg_replace('#.*?#s', '', $content); + $content = preg_replace('#]+>#s', '', $content); + + $chunks = preg_split('##', $content, -1, PREG_SPLIT_DELIM_CAPTURE); + $chunks[0] = str_replace($this->phpEscapeMap[0], $this->phpEscapeMap[1], $chunks[0]); + + $i = 1; + while (isset($chunks[$i])) { + $options = array(); + preg_match_all('/(src|onerror|alt)="([^"]*?)"/', $chunks[$i], $matches, PREG_SET_ORDER); + foreach ($matches as $set) { + $options[$set[1]] = $set[2]; + } + + if (!isset($options['src'])) { + throw new \RuntimeException('Unable to process an ESI tag without a "src" attribute.'); + } + + $chunks[$i] = sprintf('surrogate->handle($this, %s, %s, %s) ?>'."\n", + var_export($options['src'], true), + var_export(isset($options['alt']) ? $options['alt'] : '', true), + isset($options['onerror']) && 'continue' === $options['onerror'] ? 'true' : 'false' + ); + ++$i; + $chunks[$i] = str_replace($this->phpEscapeMap[0], $this->phpEscapeMap[1], $chunks[$i]); + ++$i; + } + $content = implode('', $chunks); + + $response->setContent($content); + $response->headers->set('X-Body-Eval', 'ESI'); + + // remove ESI/1.0 from the Surrogate-Control header + $this->removeFromControl($response); + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/HttpCache/HttpCache.php b/lib/silex/vendor/symfony/http-kernel/HttpCache/HttpCache.php new file mode 100644 index 000000000..7c5a44f05 --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/HttpCache/HttpCache.php @@ -0,0 +1,714 @@ + + * + * This code is partially based on the Rack-Cache library by Ryan Tomayko, + * which is released under the MIT license. + * (based on commit 02d2b48d75bcb63cf1c0c7149c077ad256542801) + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\HttpCache; + +use Symfony\Component\HttpKernel\HttpKernelInterface; +use Symfony\Component\HttpKernel\TerminableInterface; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +/** + * Cache provides HTTP caching. + * + * @author Fabien Potencier + */ +class HttpCache implements HttpKernelInterface, TerminableInterface +{ + private $kernel; + private $store; + private $request; + private $surrogate; + private $surrogateCacheStrategy; + private $options = array(); + private $traces = array(); + + /** + * Constructor. + * + * The available options are: + * + * * debug: If true, the traces are added as a HTTP header to ease debugging + * + * * default_ttl The number of seconds that a cache entry should be considered + * fresh when no explicit freshness information is provided in + * a response. Explicit Cache-Control or Expires headers + * override this value. (default: 0) + * + * * private_headers Set of request headers that trigger "private" cache-control behavior + * on responses that don't explicitly state whether the response is + * public or private via a Cache-Control directive. (default: Authorization and Cookie) + * + * * allow_reload Specifies whether the client can force a cache reload by including a + * Cache-Control "no-cache" directive in the request. Set it to ``true`` + * for compliance with RFC 2616. (default: false) + * + * * allow_revalidate Specifies whether the client can force a cache revalidate by including + * a Cache-Control "max-age=0" directive in the request. Set it to ``true`` + * for compliance with RFC 2616. (default: false) + * + * * stale_while_revalidate Specifies the default number of seconds (the granularity is the second as the + * Response TTL precision is a second) during which the cache can immediately return + * a stale response while it revalidates it in the background (default: 2). + * This setting is overridden by the stale-while-revalidate HTTP Cache-Control + * extension (see RFC 5861). + * + * * stale_if_error Specifies the default number of seconds (the granularity is the second) during which + * the cache can serve a stale response when an error is encountered (default: 60). + * This setting is overridden by the stale-if-error HTTP Cache-Control extension + * (see RFC 5861). + */ + public function __construct(HttpKernelInterface $kernel, StoreInterface $store, SurrogateInterface $surrogate = null, array $options = array()) + { + $this->store = $store; + $this->kernel = $kernel; + $this->surrogate = $surrogate; + + // needed in case there is a fatal error because the backend is too slow to respond + register_shutdown_function(array($this->store, 'cleanup')); + + $this->options = array_merge(array( + 'debug' => false, + 'default_ttl' => 0, + 'private_headers' => array('Authorization', 'Cookie'), + 'allow_reload' => false, + 'allow_revalidate' => false, + 'stale_while_revalidate' => 2, + 'stale_if_error' => 60, + ), $options); + } + + /** + * Gets the current store. + * + * @return StoreInterface $store A StoreInterface instance + */ + public function getStore() + { + return $this->store; + } + + /** + * Returns an array of events that took place during processing of the last request. + * + * @return array An array of events + */ + public function getTraces() + { + return $this->traces; + } + + /** + * Returns a log message for the events of the last request processing. + * + * @return string A log message + */ + public function getLog() + { + $log = array(); + foreach ($this->traces as $request => $traces) { + $log[] = sprintf('%s: %s', $request, implode(', ', $traces)); + } + + return implode('; ', $log); + } + + /** + * Gets the Request instance associated with the master request. + * + * @return Request A Request instance + */ + public function getRequest() + { + return $this->request; + } + + /** + * Gets the Kernel instance. + * + * @return HttpKernelInterface An HttpKernelInterface instance + */ + public function getKernel() + { + return $this->kernel; + } + + /** + * Gets the Surrogate instance. + * + * @return SurrogateInterface A Surrogate instance + * + * @throws \LogicException + */ + public function getSurrogate() + { + return $this->surrogate; + } + + /** + * {@inheritdoc} + */ + public function handle(Request $request, $type = HttpKernelInterface::MASTER_REQUEST, $catch = true) + { + // FIXME: catch exceptions and implement a 500 error page here? -> in Varnish, there is a built-in error page mechanism + if (HttpKernelInterface::MASTER_REQUEST === $type) { + $this->traces = array(); + $this->request = $request; + if (null !== $this->surrogate) { + $this->surrogateCacheStrategy = $this->surrogate->createCacheStrategy(); + } + } + + $this->traces[$this->getTraceKey($request)] = array(); + + if (!$request->isMethodSafe(false)) { + $response = $this->invalidate($request, $catch); + } elseif ($request->headers->has('expect') || !$request->isMethodCacheable()) { + $response = $this->pass($request, $catch); + } elseif ($this->options['allow_reload'] && $request->isNoCache()) { + /* + If allow_reload is configured and the client requests "Cache-Control: no-cache", + reload the cache by fetching a fresh response and caching it (if possible). + */ + $this->record($request, 'reload'); + $response = $this->fetch($request, $catch); + } else { + $response = $this->lookup($request, $catch); + } + + $this->restoreResponseBody($request, $response); + + if (HttpKernelInterface::MASTER_REQUEST === $type && $this->options['debug']) { + $response->headers->set('X-Symfony-Cache', $this->getLog()); + } + + if (null !== $this->surrogate) { + if (HttpKernelInterface::MASTER_REQUEST === $type) { + $this->surrogateCacheStrategy->update($response); + } else { + $this->surrogateCacheStrategy->add($response); + } + } + + $response->prepare($request); + + $response->isNotModified($request); + + return $response; + } + + /** + * {@inheritdoc} + */ + public function terminate(Request $request, Response $response) + { + if ($this->getKernel() instanceof TerminableInterface) { + $this->getKernel()->terminate($request, $response); + } + } + + /** + * Forwards the Request to the backend without storing the Response in the cache. + * + * @param Request $request A Request instance + * @param bool $catch Whether to process exceptions + * + * @return Response A Response instance + */ + protected function pass(Request $request, $catch = false) + { + $this->record($request, 'pass'); + + return $this->forward($request, $catch); + } + + /** + * Invalidates non-safe methods (like POST, PUT, and DELETE). + * + * @param Request $request A Request instance + * @param bool $catch Whether to process exceptions + * + * @return Response A Response instance + * + * @throws \Exception + * + * @see RFC2616 13.10 + */ + protected function invalidate(Request $request, $catch = false) + { + $response = $this->pass($request, $catch); + + // invalidate only when the response is successful + if ($response->isSuccessful() || $response->isRedirect()) { + try { + $this->store->invalidate($request); + + // As per the RFC, invalidate Location and Content-Location URLs if present + foreach (array('Location', 'Content-Location') as $header) { + if ($uri = $response->headers->get($header)) { + $subRequest = Request::create($uri, 'get', array(), array(), array(), $request->server->all()); + + $this->store->invalidate($subRequest); + } + } + + $this->record($request, 'invalidate'); + } catch (\Exception $e) { + $this->record($request, 'invalidate-failed'); + + if ($this->options['debug']) { + throw $e; + } + } + } + + return $response; + } + + /** + * Lookups a Response from the cache for the given Request. + * + * When a matching cache entry is found and is fresh, it uses it as the + * response without forwarding any request to the backend. When a matching + * cache entry is found but is stale, it attempts to "validate" the entry with + * the backend using conditional GET. When no matching cache entry is found, + * it triggers "miss" processing. + * + * @param Request $request A Request instance + * @param bool $catch Whether to process exceptions + * + * @return Response A Response instance + * + * @throws \Exception + */ + protected function lookup(Request $request, $catch = false) + { + try { + $entry = $this->store->lookup($request); + } catch (\Exception $e) { + $this->record($request, 'lookup-failed'); + + if ($this->options['debug']) { + throw $e; + } + + return $this->pass($request, $catch); + } + + if (null === $entry) { + $this->record($request, 'miss'); + + return $this->fetch($request, $catch); + } + + if (!$this->isFreshEnough($request, $entry)) { + $this->record($request, 'stale'); + + return $this->validate($request, $entry, $catch); + } + + $this->record($request, 'fresh'); + + $entry->headers->set('Age', $entry->getAge()); + + return $entry; + } + + /** + * Validates that a cache entry is fresh. + * + * The original request is used as a template for a conditional + * GET request with the backend. + * + * @param Request $request A Request instance + * @param Response $entry A Response instance to validate + * @param bool $catch Whether to process exceptions + * + * @return Response A Response instance + */ + protected function validate(Request $request, Response $entry, $catch = false) + { + $subRequest = clone $request; + + // send no head requests because we want content + if ('HEAD' === $request->getMethod()) { + $subRequest->setMethod('GET'); + } + + // add our cached last-modified validator + $subRequest->headers->set('if_modified_since', $entry->headers->get('Last-Modified')); + + // Add our cached etag validator to the environment. + // We keep the etags from the client to handle the case when the client + // has a different private valid entry which is not cached here. + $cachedEtags = $entry->getEtag() ? array($entry->getEtag()) : array(); + $requestEtags = $request->getETags(); + if ($etags = array_unique(array_merge($cachedEtags, $requestEtags))) { + $subRequest->headers->set('if_none_match', implode(', ', $etags)); + } + + $response = $this->forward($subRequest, $catch, $entry); + + if (304 == $response->getStatusCode()) { + $this->record($request, 'valid'); + + // return the response and not the cache entry if the response is valid but not cached + $etag = $response->getEtag(); + if ($etag && in_array($etag, $requestEtags) && !in_array($etag, $cachedEtags)) { + return $response; + } + + $entry = clone $entry; + $entry->headers->remove('Date'); + + foreach (array('Date', 'Expires', 'Cache-Control', 'ETag', 'Last-Modified') as $name) { + if ($response->headers->has($name)) { + $entry->headers->set($name, $response->headers->get($name)); + } + } + + $response = $entry; + } else { + $this->record($request, 'invalid'); + } + + if ($response->isCacheable()) { + $this->store($request, $response); + } + + return $response; + } + + /** + * Unconditionally fetches a fresh response from the backend and + * stores it in the cache if is cacheable. + * + * @param Request $request A Request instance + * @param bool $catch Whether to process exceptions + * + * @return Response A Response instance + */ + protected function fetch(Request $request, $catch = false) + { + $subRequest = clone $request; + + // send no head requests because we want content + if ('HEAD' === $request->getMethod()) { + $subRequest->setMethod('GET'); + } + + // avoid that the backend sends no content + $subRequest->headers->remove('if_modified_since'); + $subRequest->headers->remove('if_none_match'); + + $response = $this->forward($subRequest, $catch); + + if ($response->isCacheable()) { + $this->store($request, $response); + } + + return $response; + } + + /** + * Forwards the Request to the backend and returns the Response. + * + * All backend requests (cache passes, fetches, cache validations) + * run through this method. + * + * @param Request $request A Request instance + * @param bool $catch Whether to catch exceptions or not + * @param Response $entry A Response instance (the stale entry if present, null otherwise) + * + * @return Response A Response instance + */ + protected function forward(Request $request, $catch = false, Response $entry = null) + { + if ($this->surrogate) { + $this->surrogate->addSurrogateCapability($request); + } + + // modify the X-Forwarded-For header if needed + $forwardedFor = $request->headers->get('X-Forwarded-For'); + if ($forwardedFor) { + $request->headers->set('X-Forwarded-For', $forwardedFor.', '.$request->server->get('REMOTE_ADDR')); + } else { + $request->headers->set('X-Forwarded-For', $request->server->get('REMOTE_ADDR')); + } + + // fix the client IP address by setting it to 127.0.0.1 as HttpCache + // is always called from the same process as the backend. + $request->server->set('REMOTE_ADDR', '127.0.0.1'); + + // make sure HttpCache is a trusted proxy + if (!in_array('127.0.0.1', $trustedProxies = Request::getTrustedProxies())) { + $trustedProxies[] = '127.0.0.1'; + Request::setTrustedProxies($trustedProxies, Request::HEADER_X_FORWARDED_ALL); + } + + // always a "master" request (as the real master request can be in cache) + $response = $this->kernel->handle($request, HttpKernelInterface::MASTER_REQUEST, $catch); + // FIXME: we probably need to also catch exceptions if raw === true + + // we don't implement the stale-if-error on Requests, which is nonetheless part of the RFC + if (null !== $entry && in_array($response->getStatusCode(), array(500, 502, 503, 504))) { + if (null === $age = $entry->headers->getCacheControlDirective('stale-if-error')) { + $age = $this->options['stale_if_error']; + } + + if (abs($entry->getTtl()) < $age) { + $this->record($request, 'stale-if-error'); + + return $entry; + } + } + + /* + RFC 7231 Sect. 7.1.1.2 says that a server that does not have a reasonably accurate + clock MUST NOT send a "Date" header, although it MUST send one in most other cases + except for 1xx or 5xx responses where it MAY do so. + + Anyway, a client that received a message without a "Date" header MUST add it. + */ + if (!$response->headers->has('Date')) { + $response->setDate(\DateTime::createFromFormat('U', time())); + } + + $this->processResponseBody($request, $response); + + if ($this->isPrivateRequest($request) && !$response->headers->hasCacheControlDirective('public')) { + $response->setPrivate(); + } elseif ($this->options['default_ttl'] > 0 && null === $response->getTtl() && !$response->headers->getCacheControlDirective('must-revalidate')) { + $response->setTtl($this->options['default_ttl']); + } + + return $response; + } + + /** + * Checks whether the cache entry is "fresh enough" to satisfy the Request. + * + * @return bool true if the cache entry if fresh enough, false otherwise + */ + protected function isFreshEnough(Request $request, Response $entry) + { + if (!$entry->isFresh()) { + return $this->lock($request, $entry); + } + + if ($this->options['allow_revalidate'] && null !== $maxAge = $request->headers->getCacheControlDirective('max-age')) { + return $maxAge > 0 && $maxAge >= $entry->getAge(); + } + + return true; + } + + /** + * Locks a Request during the call to the backend. + * + * @return bool true if the cache entry can be returned even if it is staled, false otherwise + */ + protected function lock(Request $request, Response $entry) + { + // try to acquire a lock to call the backend + $lock = $this->store->lock($request); + + if (true === $lock) { + // we have the lock, call the backend + return false; + } + + // there is already another process calling the backend + + // May we serve a stale response? + if ($this->mayServeStaleWhileRevalidate($entry)) { + $this->record($request, 'stale-while-revalidate'); + + return true; + } + + // wait for the lock to be released + if ($this->waitForLock($request)) { + // replace the current entry with the fresh one + $new = $this->lookup($request); + $entry->headers = $new->headers; + $entry->setContent($new->getContent()); + $entry->setStatusCode($new->getStatusCode()); + $entry->setProtocolVersion($new->getProtocolVersion()); + foreach ($new->headers->getCookies() as $cookie) { + $entry->headers->setCookie($cookie); + } + } else { + // backend is slow as hell, send a 503 response (to avoid the dog pile effect) + $entry->setStatusCode(503); + $entry->setContent('503 Service Unavailable'); + $entry->headers->set('Retry-After', 10); + } + + return true; + } + + /** + * Writes the Response to the cache. + * + * @throws \Exception + */ + protected function store(Request $request, Response $response) + { + try { + $this->store->write($request, $response); + + $this->record($request, 'store'); + + $response->headers->set('Age', $response->getAge()); + } catch (\Exception $e) { + $this->record($request, 'store-failed'); + + if ($this->options['debug']) { + throw $e; + } + } + + // now that the response is cached, release the lock + $this->store->unlock($request); + } + + /** + * Restores the Response body. + */ + private function restoreResponseBody(Request $request, Response $response) + { + if ($response->headers->has('X-Body-Eval')) { + ob_start(); + + if ($response->headers->has('X-Body-File')) { + include $response->headers->get('X-Body-File'); + } else { + eval('; ?>'.$response->getContent().'setContent(ob_get_clean()); + $response->headers->remove('X-Body-Eval'); + if (!$response->headers->has('Transfer-Encoding')) { + $response->headers->set('Content-Length', strlen($response->getContent())); + } + } elseif ($response->headers->has('X-Body-File')) { + // Response does not include possibly dynamic content (ESI, SSI), so we need + // not handle the content for HEAD requests + if (!$request->isMethod('HEAD')) { + $response->setContent(file_get_contents($response->headers->get('X-Body-File'))); + } + } else { + return; + } + + $response->headers->remove('X-Body-File'); + } + + protected function processResponseBody(Request $request, Response $response) + { + if (null !== $this->surrogate && $this->surrogate->needsParsing($response)) { + $this->surrogate->process($request, $response); + } + } + + /** + * Checks if the Request includes authorization or other sensitive information + * that should cause the Response to be considered private by default. + * + * @return bool true if the Request is private, false otherwise + */ + private function isPrivateRequest(Request $request) + { + foreach ($this->options['private_headers'] as $key) { + $key = strtolower(str_replace('HTTP_', '', $key)); + + if ('cookie' === $key) { + if (count($request->cookies->all())) { + return true; + } + } elseif ($request->headers->has($key)) { + return true; + } + } + + return false; + } + + /** + * Records that an event took place. + * + * @param Request $request A Request instance + * @param string $event The event name + */ + private function record(Request $request, $event) + { + $this->traces[$this->getTraceKey($request)][] = $event; + } + + /** + * Calculates the key we use in the "trace" array for a given request. + * + * @param Request $request + * + * @return string + */ + private function getTraceKey(Request $request) + { + $path = $request->getPathInfo(); + if ($qs = $request->getQueryString()) { + $path .= '?'.$qs; + } + + return $request->getMethod().' '.$path; + } + + /** + * Checks whether the given (cached) response may be served as "stale" when a revalidation + * is currently in progress. + * + * @param Response $entry + * + * @return bool true when the stale response may be served, false otherwise + */ + private function mayServeStaleWhileRevalidate(Response $entry) + { + $timeout = $entry->headers->getCacheControlDirective('stale-while-revalidate'); + + if (null === $timeout) { + $timeout = $this->options['stale_while_revalidate']; + } + + return abs($entry->getTtl()) < $timeout; + } + + /** + * Waits for the store to release a locked entry. + * + * @param Request $request The request to wait for + * + * @return bool true if the lock was released before the internal timeout was hit; false if the wait timeout was exceeded + */ + private function waitForLock(Request $request) + { + $wait = 0; + while ($this->store->isLocked($request) && $wait < 100) { + usleep(50000); + ++$wait; + } + + return $wait < 100; + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/HttpCache/ResponseCacheStrategy.php b/lib/silex/vendor/symfony/http-kernel/HttpCache/ResponseCacheStrategy.php new file mode 100644 index 000000000..027b2b176 --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/HttpCache/ResponseCacheStrategy.php @@ -0,0 +1,96 @@ + + * + * This code is partially based on the Rack-Cache library by Ryan Tomayko, + * which is released under the MIT license. + * (based on commit 02d2b48d75bcb63cf1c0c7149c077ad256542801) + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\HttpCache; + +use Symfony\Component\HttpFoundation\Response; + +/** + * ResponseCacheStrategy knows how to compute the Response cache HTTP header + * based on the different response cache headers. + * + * This implementation changes the master response TTL to the smallest TTL received + * or force validation if one of the surrogates has validation cache strategy. + * + * @author Fabien Potencier + */ +class ResponseCacheStrategy implements ResponseCacheStrategyInterface +{ + private $cacheable = true; + private $embeddedResponses = 0; + private $ttls = array(); + private $maxAges = array(); + private $isNotCacheableResponseEmbedded = false; + + /** + * {@inheritdoc} + */ + public function add(Response $response) + { + if (!$response->isFresh() || !$response->isCacheable()) { + $this->cacheable = false; + } else { + $maxAge = $response->getMaxAge(); + $this->ttls[] = $response->getTtl(); + $this->maxAges[] = $maxAge; + + if (null === $maxAge) { + $this->isNotCacheableResponseEmbedded = true; + } + } + + ++$this->embeddedResponses; + } + + /** + * {@inheritdoc} + */ + public function update(Response $response) + { + // if we have no embedded Response, do nothing + if (0 === $this->embeddedResponses) { + return; + } + + // Remove validation related headers in order to avoid browsers using + // their own cache, because some of the response content comes from + // at least one embedded response (which likely has a different caching strategy). + if ($response->isValidateable()) { + $response->setEtag(null); + $response->setLastModified(null); + } + + if (!$response->isFresh()) { + $this->cacheable = false; + } + + if (!$this->cacheable) { + $response->headers->set('Cache-Control', 'no-cache, must-revalidate'); + + return; + } + + $this->ttls[] = $response->getTtl(); + $this->maxAges[] = $response->getMaxAge(); + + if ($this->isNotCacheableResponseEmbedded) { + $response->headers->removeCacheControlDirective('s-maxage'); + } elseif (null !== $maxAge = min($this->maxAges)) { + $response->setSharedMaxAge($maxAge); + $response->headers->set('Age', $maxAge - min($this->ttls)); + } + $response->setMaxAge(0); + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/HttpCache/ResponseCacheStrategyInterface.php b/lib/silex/vendor/symfony/http-kernel/HttpCache/ResponseCacheStrategyInterface.php new file mode 100644 index 000000000..e282299ae --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/HttpCache/ResponseCacheStrategyInterface.php @@ -0,0 +1,37 @@ + + * + * This code is partially based on the Rack-Cache library by Ryan Tomayko, + * which is released under the MIT license. + * (based on commit 02d2b48d75bcb63cf1c0c7149c077ad256542801) + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\HttpCache; + +use Symfony\Component\HttpFoundation\Response; + +/** + * ResponseCacheStrategyInterface implementations know how to compute the + * Response cache HTTP header based on the different response cache headers. + * + * @author Fabien Potencier + */ +interface ResponseCacheStrategyInterface +{ + /** + * Adds a Response. + */ + public function add(Response $response); + + /** + * Updates the Response HTTP headers based on the embedded Responses. + */ + public function update(Response $response); +} diff --git a/lib/silex/vendor/symfony/http-kernel/HttpCache/Ssi.php b/lib/silex/vendor/symfony/http-kernel/HttpCache/Ssi.php new file mode 100644 index 000000000..3178c3351 --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/HttpCache/Ssi.php @@ -0,0 +1,98 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\HttpCache; + +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +/** + * Ssi implements the SSI capabilities to Request and Response instances. + * + * @author Sebastian Krebs + */ +class Ssi extends AbstractSurrogate +{ + /** + * {@inheritdoc} + */ + public function getName() + { + return 'ssi'; + } + + /** + * {@inheritdoc} + */ + public function addSurrogateControl(Response $response) + { + if (false !== strpos($response->getContent(), '', $uri); + } + + /** + * {@inheritdoc} + */ + public function process(Request $request, Response $response) + { + $type = $response->headers->get('Content-Type'); + if (empty($type)) { + $type = 'text/html'; + } + + $parts = explode(';', $type); + if (!in_array($parts[0], $this->contentTypes)) { + return $response; + } + + // we don't use a proper XML parser here as we can have SSI tags in a plain text response + $content = $response->getContent(); + + $chunks = preg_split('##', $content, -1, PREG_SPLIT_DELIM_CAPTURE); + $chunks[0] = str_replace($this->phpEscapeMap[0], $this->phpEscapeMap[1], $chunks[0]); + + $i = 1; + while (isset($chunks[$i])) { + $options = array(); + preg_match_all('/(virtual)="([^"]*?)"/', $chunks[$i], $matches, PREG_SET_ORDER); + foreach ($matches as $set) { + $options[$set[1]] = $set[2]; + } + + if (!isset($options['virtual'])) { + throw new \RuntimeException('Unable to process an SSI tag without a "virtual" attribute.'); + } + + $chunks[$i] = sprintf('surrogate->handle($this, %s, \'\', false) ?>'."\n", + var_export($options['virtual'], true) + ); + ++$i; + $chunks[$i] = str_replace($this->phpEscapeMap[0], $this->phpEscapeMap[1], $chunks[$i]); + ++$i; + } + $content = implode('', $chunks); + + $response->setContent($content); + $response->headers->set('X-Body-Eval', 'SSI'); + + // remove SSI/1.0 from the Surrogate-Control header + $this->removeFromControl($response); + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/HttpCache/Store.php b/lib/silex/vendor/symfony/http-kernel/HttpCache/Store.php new file mode 100644 index 000000000..fa0d4a867 --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/HttpCache/Store.php @@ -0,0 +1,485 @@ + + * + * This code is partially based on the Rack-Cache library by Ryan Tomayko, + * which is released under the MIT license. + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\HttpCache; + +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +/** + * Store implements all the logic for storing cache metadata (Request and Response headers). + * + * @author Fabien Potencier + */ +class Store implements StoreInterface +{ + protected $root; + private $keyCache; + private $locks; + + /** + * @param string $root The path to the cache directory + * + * @throws \RuntimeException + */ + public function __construct($root) + { + $this->root = $root; + if (!file_exists($this->root) && !@mkdir($this->root, 0777, true) && !is_dir($this->root)) { + throw new \RuntimeException(sprintf('Unable to create the store directory (%s).', $this->root)); + } + $this->keyCache = new \SplObjectStorage(); + $this->locks = array(); + } + + /** + * Cleanups storage. + */ + public function cleanup() + { + // unlock everything + foreach ($this->locks as $lock) { + flock($lock, LOCK_UN); + fclose($lock); + } + + $this->locks = array(); + } + + /** + * Tries to lock the cache for a given Request, without blocking. + * + * @return bool|string true if the lock is acquired, the path to the current lock otherwise + */ + public function lock(Request $request) + { + $key = $this->getCacheKey($request); + + if (!isset($this->locks[$key])) { + $path = $this->getPath($key); + if (!file_exists(dirname($path)) && false === @mkdir(dirname($path), 0777, true) && !is_dir(dirname($path))) { + return $path; + } + $h = fopen($path, 'cb'); + if (!flock($h, LOCK_EX | LOCK_NB)) { + fclose($h); + + return $path; + } + + $this->locks[$key] = $h; + } + + return true; + } + + /** + * Releases the lock for the given Request. + * + * @return bool False if the lock file does not exist or cannot be unlocked, true otherwise + */ + public function unlock(Request $request) + { + $key = $this->getCacheKey($request); + + if (isset($this->locks[$key])) { + flock($this->locks[$key], LOCK_UN); + fclose($this->locks[$key]); + unset($this->locks[$key]); + + return true; + } + + return false; + } + + public function isLocked(Request $request) + { + $key = $this->getCacheKey($request); + + if (isset($this->locks[$key])) { + return true; // shortcut if lock held by this process + } + + if (!file_exists($path = $this->getPath($key))) { + return false; + } + + $h = fopen($path, 'rb'); + flock($h, LOCK_EX | LOCK_NB, $wouldBlock); + flock($h, LOCK_UN); // release the lock we just acquired + fclose($h); + + return (bool) $wouldBlock; + } + + /** + * Locates a cached Response for the Request provided. + * + * @return Response|null A Response instance, or null if no cache entry was found + */ + public function lookup(Request $request) + { + $key = $this->getCacheKey($request); + + if (!$entries = $this->getMetadata($key)) { + return; + } + + // find a cached entry that matches the request. + $match = null; + foreach ($entries as $entry) { + if ($this->requestsMatch(isset($entry[1]['vary'][0]) ? implode(', ', $entry[1]['vary']) : '', $request->headers->all(), $entry[0])) { + $match = $entry; + + break; + } + } + + if (null === $match) { + return; + } + + $headers = $match[1]; + if (file_exists($body = $this->getPath($headers['x-content-digest'][0]))) { + return $this->restoreResponse($headers, $body); + } + + // TODO the metaStore referenced an entity that doesn't exist in + // the entityStore. We definitely want to return nil but we should + // also purge the entry from the meta-store when this is detected. + } + + /** + * Writes a cache entry to the store for the given Request and Response. + * + * Existing entries are read and any that match the response are removed. This + * method calls write with the new list of cache entries. + * + * @return string The key under which the response is stored + * + * @throws \RuntimeException + */ + public function write(Request $request, Response $response) + { + $key = $this->getCacheKey($request); + $storedEnv = $this->persistRequest($request); + + // write the response body to the entity store if this is the original response + if (!$response->headers->has('X-Content-Digest')) { + $digest = $this->generateContentDigest($response); + + if (false === $this->save($digest, $response->getContent())) { + throw new \RuntimeException('Unable to store the entity.'); + } + + $response->headers->set('X-Content-Digest', $digest); + + if (!$response->headers->has('Transfer-Encoding')) { + $response->headers->set('Content-Length', strlen($response->getContent())); + } + } + + // read existing cache entries, remove non-varying, and add this one to the list + $entries = array(); + $vary = $response->headers->get('vary'); + foreach ($this->getMetadata($key) as $entry) { + if (!isset($entry[1]['vary'][0])) { + $entry[1]['vary'] = array(''); + } + + if ($entry[1]['vary'][0] != $vary || !$this->requestsMatch($vary, $entry[0], $storedEnv)) { + $entries[] = $entry; + } + } + + $headers = $this->persistResponse($response); + unset($headers['age']); + + array_unshift($entries, array($storedEnv, $headers)); + + if (false === $this->save($key, serialize($entries))) { + throw new \RuntimeException('Unable to store the metadata.'); + } + + return $key; + } + + /** + * Returns content digest for $response. + * + * @return string + */ + protected function generateContentDigest(Response $response) + { + return 'en'.hash('sha256', $response->getContent()); + } + + /** + * Invalidates all cache entries that match the request. + * + * @throws \RuntimeException + */ + public function invalidate(Request $request) + { + $modified = false; + $key = $this->getCacheKey($request); + + $entries = array(); + foreach ($this->getMetadata($key) as $entry) { + $response = $this->restoreResponse($entry[1]); + if ($response->isFresh()) { + $response->expire(); + $modified = true; + $entries[] = array($entry[0], $this->persistResponse($response)); + } else { + $entries[] = $entry; + } + } + + if ($modified && false === $this->save($key, serialize($entries))) { + throw new \RuntimeException('Unable to store the metadata.'); + } + } + + /** + * Determines whether two Request HTTP header sets are non-varying based on + * the vary response header value provided. + * + * @param string $vary A Response vary header + * @param array $env1 A Request HTTP header array + * @param array $env2 A Request HTTP header array + * + * @return bool true if the two environments match, false otherwise + */ + private function requestsMatch($vary, $env1, $env2) + { + if (empty($vary)) { + return true; + } + + foreach (preg_split('/[\s,]+/', $vary) as $header) { + $key = str_replace('_', '-', strtolower($header)); + $v1 = isset($env1[$key]) ? $env1[$key] : null; + $v2 = isset($env2[$key]) ? $env2[$key] : null; + if ($v1 !== $v2) { + return false; + } + } + + return true; + } + + /** + * Gets all data associated with the given key. + * + * Use this method only if you know what you are doing. + * + * @param string $key The store key + * + * @return array An array of data associated with the key + */ + private function getMetadata($key) + { + if (!$entries = $this->load($key)) { + return array(); + } + + return unserialize($entries); + } + + /** + * Purges data for the given URL. + * + * This method purges both the HTTP and the HTTPS version of the cache entry. + * + * @param string $url A URL + * + * @return bool true if the URL exists with either HTTP or HTTPS scheme and has been purged, false otherwise + */ + public function purge($url) + { + $http = preg_replace('#^https:#', 'http:', $url); + $https = preg_replace('#^http:#', 'https:', $url); + + $purgedHttp = $this->doPurge($http); + $purgedHttps = $this->doPurge($https); + + return $purgedHttp || $purgedHttps; + } + + /** + * Purges data for the given URL. + * + * @param string $url A URL + * + * @return bool true if the URL exists and has been purged, false otherwise + */ + private function doPurge($url) + { + $key = $this->getCacheKey(Request::create($url)); + if (isset($this->locks[$key])) { + flock($this->locks[$key], LOCK_UN); + fclose($this->locks[$key]); + unset($this->locks[$key]); + } + + if (file_exists($path = $this->getPath($key))) { + unlink($path); + + return true; + } + + return false; + } + + /** + * Loads data for the given key. + * + * @param string $key The store key + * + * @return string The data associated with the key + */ + private function load($key) + { + $path = $this->getPath($key); + + return file_exists($path) ? file_get_contents($path) : false; + } + + /** + * Save data for the given key. + * + * @param string $key The store key + * @param string $data The data to store + * + * @return bool + */ + private function save($key, $data) + { + $path = $this->getPath($key); + + if (isset($this->locks[$key])) { + $fp = $this->locks[$key]; + @ftruncate($fp, 0); + @fseek($fp, 0); + $len = @fwrite($fp, $data); + if (strlen($data) !== $len) { + @ftruncate($fp, 0); + + return false; + } + } else { + if (!file_exists(dirname($path)) && false === @mkdir(dirname($path), 0777, true) && !is_dir(dirname($path))) { + return false; + } + + $tmpFile = tempnam(dirname($path), basename($path)); + if (false === $fp = @fopen($tmpFile, 'wb')) { + return false; + } + @fwrite($fp, $data); + @fclose($fp); + + if ($data != file_get_contents($tmpFile)) { + return false; + } + + if (false === @rename($tmpFile, $path)) { + return false; + } + } + + @chmod($path, 0666 & ~umask()); + } + + public function getPath($key) + { + return $this->root.DIRECTORY_SEPARATOR.substr($key, 0, 2).DIRECTORY_SEPARATOR.substr($key, 2, 2).DIRECTORY_SEPARATOR.substr($key, 4, 2).DIRECTORY_SEPARATOR.substr($key, 6); + } + + /** + * Generates a cache key for the given Request. + * + * This method should return a key that must only depend on a + * normalized version of the request URI. + * + * If the same URI can have more than one representation, based on some + * headers, use a Vary header to indicate them, and each representation will + * be stored independently under the same cache key. + * + * @return string A key for the given Request + */ + protected function generateCacheKey(Request $request) + { + return 'md'.hash('sha256', $request->getUri()); + } + + /** + * Returns a cache key for the given Request. + * + * @return string A key for the given Request + */ + private function getCacheKey(Request $request) + { + if (isset($this->keyCache[$request])) { + return $this->keyCache[$request]; + } + + return $this->keyCache[$request] = $this->generateCacheKey($request); + } + + /** + * Persists the Request HTTP headers. + * + * @return array An array of HTTP headers + */ + private function persistRequest(Request $request) + { + return $request->headers->all(); + } + + /** + * Persists the Response HTTP headers. + * + * @return array An array of HTTP headers + */ + private function persistResponse(Response $response) + { + $headers = $response->headers->all(); + $headers['X-Status'] = array($response->getStatusCode()); + + return $headers; + } + + /** + * Restores a Response from the HTTP headers and body. + * + * @param array $headers An array of HTTP headers for the Response + * @param string $body The Response body + * + * @return Response + */ + private function restoreResponse($headers, $body = null) + { + $status = $headers['X-Status'][0]; + unset($headers['X-Status']); + + if (null !== $body) { + $headers['X-Body-File'] = array($body); + } + + return new Response($body, $status, $headers); + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/HttpCache/StoreInterface.php b/lib/silex/vendor/symfony/http-kernel/HttpCache/StoreInterface.php new file mode 100644 index 000000000..8f1cf4409 --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/HttpCache/StoreInterface.php @@ -0,0 +1,83 @@ + + * + * This code is partially based on the Rack-Cache library by Ryan Tomayko, + * which is released under the MIT license. + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\HttpCache; + +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +/** + * Interface implemented by HTTP cache stores. + * + * @author Fabien Potencier + */ +interface StoreInterface +{ + /** + * Locates a cached Response for the Request provided. + * + * @return Response|null A Response instance, or null if no cache entry was found + */ + public function lookup(Request $request); + + /** + * Writes a cache entry to the store for the given Request and Response. + * + * Existing entries are read and any that match the response are removed. This + * method calls write with the new list of cache entries. + * + * @return string The key under which the response is stored + */ + public function write(Request $request, Response $response); + + /** + * Invalidates all cache entries that match the request. + */ + public function invalidate(Request $request); + + /** + * Locks the cache for a given Request. + * + * @return bool|string true if the lock is acquired, the path to the current lock otherwise + */ + public function lock(Request $request); + + /** + * Releases the lock for the given Request. + * + * @return bool False if the lock file does not exist or cannot be unlocked, true otherwise + */ + public function unlock(Request $request); + + /** + * Returns whether or not a lock exists. + * + * @return bool true if lock exists, false otherwise + */ + public function isLocked(Request $request); + + /** + * Purges data for the given URL. + * + * @param string $url A URL + * + * @return bool true if the URL exists and has been purged, false otherwise + */ + public function purge($url); + + /** + * Cleanups storage. + */ + public function cleanup(); +} diff --git a/lib/silex/vendor/symfony/http-kernel/HttpCache/SurrogateInterface.php b/lib/silex/vendor/symfony/http-kernel/HttpCache/SurrogateInterface.php new file mode 100644 index 000000000..85391f8f3 --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/HttpCache/SurrogateInterface.php @@ -0,0 +1,92 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\HttpCache; + +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +interface SurrogateInterface +{ + /** + * Returns surrogate name. + * + * @return string + */ + public function getName(); + + /** + * Returns a new cache strategy instance. + * + * @return ResponseCacheStrategyInterface A ResponseCacheStrategyInterface instance + */ + public function createCacheStrategy(); + + /** + * Checks that at least one surrogate has Surrogate capability. + * + * @return bool true if one surrogate has Surrogate capability, false otherwise + */ + public function hasSurrogateCapability(Request $request); + + /** + * Adds Surrogate-capability to the given Request. + */ + public function addSurrogateCapability(Request $request); + + /** + * Adds HTTP headers to specify that the Response needs to be parsed for Surrogate. + * + * This method only adds an Surrogate HTTP header if the Response has some Surrogate tags. + */ + public function addSurrogateControl(Response $response); + + /** + * Checks that the Response needs to be parsed for Surrogate tags. + * + * @return bool true if the Response needs to be parsed, false otherwise + */ + public function needsParsing(Response $response); + + /** + * Renders a Surrogate tag. + * + * @param string $uri A URI + * @param string $alt An alternate URI + * @param bool $ignoreErrors Whether to ignore errors or not + * @param string $comment A comment to add as an esi:include tag + * + * @return string + */ + public function renderIncludeTag($uri, $alt = null, $ignoreErrors = true, $comment = ''); + + /** + * Replaces a Response Surrogate tags with the included resource content. + * + * @return Response + */ + public function process(Request $request, Response $response); + + /** + * Handles a Surrogate from the cache. + * + * @param HttpCache $cache An HttpCache instance + * @param string $uri The main URI + * @param string $alt An alternative URI + * @param bool $ignoreErrors Whether to ignore errors or not + * + * @return string + * + * @throws \RuntimeException + * @throws \Exception + */ + public function handle(HttpCache $cache, $uri, $alt, $ignoreErrors); +} diff --git a/lib/silex/vendor/symfony/http-kernel/HttpKernel.php b/lib/silex/vendor/symfony/http-kernel/HttpKernel.php new file mode 100644 index 000000000..41f8913be --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/HttpKernel.php @@ -0,0 +1,299 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel; + +use Symfony\Component\HttpKernel\Controller\ArgumentResolver; +use Symfony\Component\HttpKernel\Controller\ArgumentResolverInterface; +use Symfony\Component\HttpKernel\Controller\ControllerResolverInterface; +use Symfony\Component\HttpKernel\Event\FilterControllerArgumentsEvent; +use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; +use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; +use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface; +use Symfony\Component\HttpKernel\Event\FilterControllerEvent; +use Symfony\Component\HttpKernel\Event\FilterResponseEvent; +use Symfony\Component\HttpKernel\Event\FinishRequestEvent; +use Symfony\Component\HttpKernel\Event\GetResponseEvent; +use Symfony\Component\HttpKernel\Event\GetResponseForControllerResultEvent; +use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent; +use Symfony\Component\HttpKernel\Event\PostResponseEvent; +use Symfony\Component\HttpFoundation\Exception\RequestExceptionInterface; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\RequestStack; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; + +/** + * HttpKernel notifies events to convert a Request object to a Response one. + * + * @author Fabien Potencier + */ +class HttpKernel implements HttpKernelInterface, TerminableInterface +{ + protected $dispatcher; + protected $resolver; + protected $requestStack; + private $argumentResolver; + + public function __construct(EventDispatcherInterface $dispatcher, ControllerResolverInterface $resolver, RequestStack $requestStack = null, ArgumentResolverInterface $argumentResolver = null) + { + $this->dispatcher = $dispatcher; + $this->resolver = $resolver; + $this->requestStack = $requestStack ?: new RequestStack(); + $this->argumentResolver = $argumentResolver; + + if (null === $this->argumentResolver) { + @trigger_error(sprintf('As of 3.1 an %s is used to resolve arguments. In 4.0 the $argumentResolver becomes the %s if no other is provided instead of using the $resolver argument.', ArgumentResolverInterface::class, ArgumentResolver::class), E_USER_DEPRECATED); + // fallback in case of deprecations + $this->argumentResolver = $resolver; + } + } + + /** + * {@inheritdoc} + */ + public function handle(Request $request, $type = HttpKernelInterface::MASTER_REQUEST, $catch = true) + { + $request->headers->set('X-Php-Ob-Level', ob_get_level()); + + try { + return $this->handleRaw($request, $type); + } catch (\Exception $e) { + if ($e instanceof RequestExceptionInterface) { + $e = new BadRequestHttpException($e->getMessage(), $e); + } + if (false === $catch) { + $this->finishRequest($request, $type); + + throw $e; + } + + return $this->handleException($e, $request, $type); + } + } + + /** + * {@inheritdoc} + */ + public function terminate(Request $request, Response $response) + { + $this->dispatcher->dispatch(KernelEvents::TERMINATE, new PostResponseEvent($this, $request, $response)); + } + + /** + * @internal + */ + public function terminateWithException(\Exception $exception, Request $request = null) + { + if (!$request = $request ?: $this->requestStack->getMasterRequest()) { + throw $exception; + } + + $response = $this->handleException($exception, $request, self::MASTER_REQUEST); + + $response->sendHeaders(); + $response->sendContent(); + + $this->terminate($request, $response); + } + + /** + * Handles a request to convert it to a response. + * + * Exceptions are not caught. + * + * @param Request $request A Request instance + * @param int $type The type of the request (one of HttpKernelInterface::MASTER_REQUEST or HttpKernelInterface::SUB_REQUEST) + * + * @return Response A Response instance + * + * @throws \LogicException If one of the listener does not behave as expected + * @throws NotFoundHttpException When controller cannot be found + */ + private function handleRaw(Request $request, $type = self::MASTER_REQUEST) + { + $this->requestStack->push($request); + + // request + $event = new GetResponseEvent($this, $request, $type); + $this->dispatcher->dispatch(KernelEvents::REQUEST, $event); + + if ($event->hasResponse()) { + return $this->filterResponse($event->getResponse(), $request, $type); + } + + // load controller + if (false === $controller = $this->resolver->getController($request)) { + throw new NotFoundHttpException(sprintf('Unable to find the controller for path "%s". The route is wrongly configured.', $request->getPathInfo())); + } + + $event = new FilterControllerEvent($this, $controller, $request, $type); + $this->dispatcher->dispatch(KernelEvents::CONTROLLER, $event); + $controller = $event->getController(); + + // controller arguments + $arguments = $this->argumentResolver->getArguments($request, $controller); + + $event = new FilterControllerArgumentsEvent($this, $controller, $arguments, $request, $type); + $this->dispatcher->dispatch(KernelEvents::CONTROLLER_ARGUMENTS, $event); + $controller = $event->getController(); + $arguments = $event->getArguments(); + + // call controller + $response = \call_user_func_array($controller, $arguments); + + // view + if (!$response instanceof Response) { + $event = new GetResponseForControllerResultEvent($this, $request, $type, $response); + $this->dispatcher->dispatch(KernelEvents::VIEW, $event); + + if ($event->hasResponse()) { + $response = $event->getResponse(); + } + + if (!$response instanceof Response) { + $msg = sprintf('The controller must return a response (%s given).', $this->varToString($response)); + + // the user may have forgotten to return something + if (null === $response) { + $msg .= ' Did you forget to add a return statement somewhere in your controller?'; + } + throw new \LogicException($msg); + } + } + + return $this->filterResponse($response, $request, $type); + } + + /** + * Filters a response object. + * + * @param Response $response A Response instance + * @param Request $request An error message in case the response is not a Response object + * @param int $type The type of the request (one of HttpKernelInterface::MASTER_REQUEST or HttpKernelInterface::SUB_REQUEST) + * + * @return Response The filtered Response instance + * + * @throws \RuntimeException if the passed object is not a Response instance + */ + private function filterResponse(Response $response, Request $request, $type) + { + $event = new FilterResponseEvent($this, $request, $type, $response); + + $this->dispatcher->dispatch(KernelEvents::RESPONSE, $event); + + $this->finishRequest($request, $type); + + return $event->getResponse(); + } + + /** + * Publishes the finish request event, then pop the request from the stack. + * + * Note that the order of the operations is important here, otherwise + * operations such as {@link RequestStack::getParentRequest()} can lead to + * weird results. + * + * @param Request $request + * @param int $type + */ + private function finishRequest(Request $request, $type) + { + $this->dispatcher->dispatch(KernelEvents::FINISH_REQUEST, new FinishRequestEvent($this, $request, $type)); + $this->requestStack->pop(); + } + + /** + * Handles an exception by trying to convert it to a Response. + * + * @param \Exception $e An \Exception instance + * @param Request $request A Request instance + * @param int $type The type of the request + * + * @return Response A Response instance + * + * @throws \Exception + */ + private function handleException(\Exception $e, $request, $type) + { + $event = new GetResponseForExceptionEvent($this, $request, $type, $e); + $this->dispatcher->dispatch(KernelEvents::EXCEPTION, $event); + + // a listener might have replaced the exception + $e = $event->getException(); + + if (!$event->hasResponse()) { + $this->finishRequest($request, $type); + + throw $e; + } + + $response = $event->getResponse(); + + // the developer asked for a specific status code + if ($response->headers->has('X-Status-Code')) { + @trigger_error(sprintf('Using the X-Status-Code header is deprecated since Symfony 3.3 and will be removed in 4.0. Use %s::allowCustomResponseCode() instead.', GetResponseForExceptionEvent::class), E_USER_DEPRECATED); + + $response->setStatusCode($response->headers->get('X-Status-Code')); + + $response->headers->remove('X-Status-Code'); + } elseif (!$event->isAllowingCustomResponseCode() && !$response->isClientError() && !$response->isServerError() && !$response->isRedirect()) { + // ensure that we actually have an error response + if ($e instanceof HttpExceptionInterface) { + // keep the HTTP status code and headers + $response->setStatusCode($e->getStatusCode()); + $response->headers->add($e->getHeaders()); + } else { + $response->setStatusCode(500); + } + } + + try { + return $this->filterResponse($response, $request, $type); + } catch (\Exception $e) { + return $response; + } + } + + private function varToString($var) + { + if (is_object($var)) { + return sprintf('Object(%s)', get_class($var)); + } + + if (is_array($var)) { + $a = array(); + foreach ($var as $k => $v) { + $a[] = sprintf('%s => %s', $k, $this->varToString($v)); + } + + return sprintf('Array(%s)', implode(', ', $a)); + } + + if (is_resource($var)) { + return sprintf('Resource(%s)', get_resource_type($var)); + } + + if (null === $var) { + return 'null'; + } + + if (false === $var) { + return 'false'; + } + + if (true === $var) { + return 'true'; + } + + return (string) $var; + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/HttpKernelInterface.php b/lib/silex/vendor/symfony/http-kernel/HttpKernelInterface.php new file mode 100644 index 000000000..5050bfcfb --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/HttpKernelInterface.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\HttpKernel; + +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +/** + * HttpKernelInterface handles a Request to convert it to a Response. + * + * @author Fabien Potencier + */ +interface HttpKernelInterface +{ + const MASTER_REQUEST = 1; + const SUB_REQUEST = 2; + + /** + * Handles a Request to convert it to a Response. + * + * When $catch is true, the implementation must catch all exceptions + * and do its best to convert them to a Response instance. + * + * @param Request $request A Request instance + * @param int $type The type of the request + * (one of HttpKernelInterface::MASTER_REQUEST or HttpKernelInterface::SUB_REQUEST) + * @param bool $catch Whether to catch exceptions or not + * + * @return Response A Response instance + * + * @throws \Exception When an Exception occurs during processing + */ + public function handle(Request $request, $type = self::MASTER_REQUEST, $catch = true); +} diff --git a/lib/silex/vendor/symfony/http-kernel/Kernel.php b/lib/silex/vendor/symfony/http-kernel/Kernel.php new file mode 100644 index 000000000..eb2364e46 --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Kernel.php @@ -0,0 +1,969 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel; + +use Symfony\Bridge\ProxyManager\LazyProxy\Instantiator\RuntimeInstantiator; +use Symfony\Bridge\ProxyManager\LazyProxy\PhpDumper\ProxyDumper; +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\Compiler\PassConfig; +use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Dumper\PhpDumper; +use Symfony\Component\DependencyInjection\Loader\XmlFileLoader; +use Symfony\Component\DependencyInjection\Loader\YamlFileLoader; +use Symfony\Component\DependencyInjection\Loader\IniFileLoader; +use Symfony\Component\DependencyInjection\Loader\PhpFileLoader; +use Symfony\Component\DependencyInjection\Loader\DirectoryLoader; +use Symfony\Component\DependencyInjection\Loader\ClosureLoader; +use Symfony\Component\Filesystem\Filesystem; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\Bundle\BundleInterface; +use Symfony\Component\HttpKernel\Config\EnvParametersResource; +use Symfony\Component\HttpKernel\Config\FileLocator; +use Symfony\Component\HttpKernel\DependencyInjection\MergeExtensionConfigurationPass; +use Symfony\Component\HttpKernel\DependencyInjection\AddAnnotatedClassesToCachePass; +use Symfony\Component\Config\Loader\GlobFileLoader; +use Symfony\Component\Config\Loader\LoaderResolver; +use Symfony\Component\Config\Loader\DelegatingLoader; +use Symfony\Component\Config\ConfigCache; +use Symfony\Component\ClassLoader\ClassCollectionLoader; + +/** + * The Kernel is the heart of the Symfony system. + * + * It manages an environment made of bundles. + * + * @author Fabien Potencier + */ +abstract class Kernel implements KernelInterface, RebootableInterface, TerminableInterface +{ + /** + * @var BundleInterface[] + */ + protected $bundles = array(); + + protected $bundleMap; + protected $container; + protected $rootDir; + protected $environment; + protected $debug; + protected $booted = false; + protected $name; + protected $startTime; + protected $loadClassCache; + + private $projectDir; + private $warmupDir; + private $requestStackSize = 0; + private $resetServices = false; + + const VERSION = '3.4.4'; + const VERSION_ID = 30404; + const MAJOR_VERSION = 3; + const MINOR_VERSION = 4; + const RELEASE_VERSION = 4; + const EXTRA_VERSION = ''; + + const END_OF_MAINTENANCE = '11/2020'; + const END_OF_LIFE = '11/2021'; + + /** + * @param string $environment The environment + * @param bool $debug Whether to enable debugging or not + */ + public function __construct($environment, $debug) + { + $this->environment = $environment; + $this->debug = (bool) $debug; + $this->rootDir = $this->getRootDir(); + $this->name = $this->getName(); + + if ($this->debug) { + $this->startTime = microtime(true); + } + } + + public function __clone() + { + if ($this->debug) { + $this->startTime = microtime(true); + } + + $this->booted = false; + $this->container = null; + $this->requestStackSize = 0; + $this->resetServices = false; + } + + /** + * Boots the current kernel. + */ + public function boot() + { + if (true === $this->booted) { + if (!$this->requestStackSize && $this->resetServices) { + if ($this->container->has('services_resetter')) { + $this->container->get('services_resetter')->reset(); + } + $this->resetServices = false; + } + + return; + } + if ($this->debug && !isset($_ENV['SHELL_VERBOSITY']) && !isset($_SERVER['SHELL_VERBOSITY'])) { + putenv('SHELL_VERBOSITY=3'); + $_ENV['SHELL_VERBOSITY'] = 3; + $_SERVER['SHELL_VERBOSITY'] = 3; + } + + if ($this->loadClassCache) { + $this->doLoadClassCache($this->loadClassCache[0], $this->loadClassCache[1]); + } + + // init bundles + $this->initializeBundles(); + + // init container + $this->initializeContainer(); + + foreach ($this->getBundles() as $bundle) { + $bundle->setContainer($this->container); + $bundle->boot(); + } + + $this->booted = true; + } + + /** + * {@inheritdoc} + */ + public function reboot($warmupDir) + { + $this->shutdown(); + $this->warmupDir = $warmupDir; + $this->boot(); + } + + /** + * {@inheritdoc} + */ + public function terminate(Request $request, Response $response) + { + if (false === $this->booted) { + return; + } + + if ($this->getHttpKernel() instanceof TerminableInterface) { + $this->getHttpKernel()->terminate($request, $response); + } + } + + /** + * {@inheritdoc} + */ + public function shutdown() + { + if (false === $this->booted) { + return; + } + + $this->booted = false; + + foreach ($this->getBundles() as $bundle) { + $bundle->shutdown(); + $bundle->setContainer(null); + } + + $this->container = null; + $this->requestStackSize = 0; + $this->resetServices = false; + } + + /** + * {@inheritdoc} + */ + public function handle(Request $request, $type = HttpKernelInterface::MASTER_REQUEST, $catch = true) + { + $this->boot(); + ++$this->requestStackSize; + $this->resetServices = true; + + try { + return $this->getHttpKernel()->handle($request, $type, $catch); + } finally { + --$this->requestStackSize; + } + } + + /** + * Gets a HTTP kernel from the container. + * + * @return HttpKernel + */ + protected function getHttpKernel() + { + return $this->container->get('http_kernel'); + } + + /** + * {@inheritdoc} + */ + public function getBundles() + { + return $this->bundles; + } + + /** + * {@inheritdoc} + */ + public function getBundle($name, $first = true/*, $noDeprecation = false */) + { + $noDeprecation = false; + if (func_num_args() >= 3) { + $noDeprecation = func_get_arg(2); + } + + if (!$first && !$noDeprecation) { + @trigger_error(sprintf('Passing "false" as the second argument to %s() is deprecated as of 3.4 and will be removed in 4.0.', __METHOD__), E_USER_DEPRECATED); + } + + if (!isset($this->bundleMap[$name])) { + throw new \InvalidArgumentException(sprintf('Bundle "%s" does not exist or it is not enabled. Maybe you forgot to add it in the registerBundles() method of your %s.php file?', $name, get_class($this))); + } + + if (true === $first) { + return $this->bundleMap[$name][0]; + } + + return $this->bundleMap[$name]; + } + + /** + * {@inheritdoc} + * + * @throws \RuntimeException if a custom resource is hidden by a resource in a derived bundle + */ + public function locateResource($name, $dir = null, $first = true) + { + if ('@' !== $name[0]) { + throw new \InvalidArgumentException(sprintf('A resource name must start with @ ("%s" given).', $name)); + } + + if (false !== strpos($name, '..')) { + throw new \RuntimeException(sprintf('File name "%s" contains invalid characters (..).', $name)); + } + + $bundleName = substr($name, 1); + $path = ''; + if (false !== strpos($bundleName, '/')) { + list($bundleName, $path) = explode('/', $bundleName, 2); + } + + $isResource = 0 === strpos($path, 'Resources') && null !== $dir; + $overridePath = substr($path, 9); + $resourceBundle = null; + $bundles = $this->getBundle($bundleName, false, true); + $files = array(); + + foreach ($bundles as $bundle) { + if ($isResource && file_exists($file = $dir.'/'.$bundle->getName().$overridePath)) { + if (null !== $resourceBundle) { + throw new \RuntimeException(sprintf('"%s" resource is hidden by a resource from the "%s" derived bundle. Create a "%s" file to override the bundle resource.', + $file, + $resourceBundle, + $dir.'/'.$bundles[0]->getName().$overridePath + )); + } + + if ($first) { + return $file; + } + $files[] = $file; + } + + if (file_exists($file = $bundle->getPath().'/'.$path)) { + if ($first && !$isResource) { + return $file; + } + $files[] = $file; + $resourceBundle = $bundle->getName(); + } + } + + if (count($files) > 0) { + return $first && $isResource ? $files[0] : $files; + } + + throw new \InvalidArgumentException(sprintf('Unable to find file "%s".', $name)); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + if (null === $this->name) { + $this->name = preg_replace('/[^a-zA-Z0-9_]+/', '', basename($this->rootDir)); + if (ctype_digit($this->name[0])) { + $this->name = '_'.$this->name; + } + } + + return $this->name; + } + + /** + * {@inheritdoc} + */ + public function getEnvironment() + { + return $this->environment; + } + + /** + * {@inheritdoc} + */ + public function isDebug() + { + return $this->debug; + } + + /** + * {@inheritdoc} + */ + public function getRootDir() + { + if (null === $this->rootDir) { + $r = new \ReflectionObject($this); + $this->rootDir = dirname($r->getFileName()); + } + + return $this->rootDir; + } + + /** + * Gets the application root dir (path of the project's composer file). + * + * @return string The project root dir + */ + public function getProjectDir() + { + if (null === $this->projectDir) { + $r = new \ReflectionObject($this); + $dir = $rootDir = dirname($r->getFileName()); + while (!file_exists($dir.'/composer.json')) { + if ($dir === dirname($dir)) { + return $this->projectDir = $rootDir; + } + $dir = dirname($dir); + } + $this->projectDir = $dir; + } + + return $this->projectDir; + } + + /** + * {@inheritdoc} + */ + public function getContainer() + { + return $this->container; + } + + /** + * Loads the PHP class cache. + * + * This methods only registers the fact that you want to load the cache classes. + * The cache will actually only be loaded when the Kernel is booted. + * + * That optimization is mainly useful when using the HttpCache class in which + * case the class cache is not loaded if the Response is in the cache. + * + * @param string $name The cache name prefix + * @param string $extension File extension of the resulting file + * + * @deprecated since version 3.3, to be removed in 4.0. The class cache is not needed anymore when using PHP 7.0. + */ + public function loadClassCache($name = 'classes', $extension = '.php') + { + if (\PHP_VERSION_ID >= 70000) { + @trigger_error(__METHOD__.'() is deprecated since Symfony 3.3, to be removed in 4.0.', E_USER_DEPRECATED); + } + + $this->loadClassCache = array($name, $extension); + } + + /** + * @internal + * + * @deprecated since version 3.3, to be removed in 4.0. + */ + public function setClassCache(array $classes) + { + if (\PHP_VERSION_ID >= 70000) { + @trigger_error(__METHOD__.'() is deprecated since Symfony 3.3, to be removed in 4.0.', E_USER_DEPRECATED); + } + + file_put_contents(($this->warmupDir ?: $this->getCacheDir()).'/classes.map', sprintf('warmupDir ?: $this->getCacheDir()).'/annotations.map', sprintf('debug ? $this->startTime : -INF; + } + + /** + * {@inheritdoc} + */ + public function getCacheDir() + { + return $this->rootDir.'/cache/'.$this->environment; + } + + /** + * {@inheritdoc} + */ + public function getLogDir() + { + return $this->rootDir.'/logs'; + } + + /** + * {@inheritdoc} + */ + public function getCharset() + { + return 'UTF-8'; + } + + /** + * @deprecated since version 3.3, to be removed in 4.0. + */ + protected function doLoadClassCache($name, $extension) + { + if (\PHP_VERSION_ID >= 70000) { + @trigger_error(__METHOD__.'() is deprecated since Symfony 3.3, to be removed in 4.0.', E_USER_DEPRECATED); + } + $cacheDir = $this->warmupDir ?: $this->getCacheDir(); + + if (!$this->booted && is_file($cacheDir.'/classes.map')) { + ClassCollectionLoader::load(include($cacheDir.'/classes.map'), $cacheDir, $name, $this->debug, false, $extension); + } + } + + /** + * Initializes the data structures related to the bundle management. + * + * - the bundles property maps a bundle name to the bundle instance, + * - the bundleMap property maps a bundle name to the bundle inheritance hierarchy (most derived bundle first). + * + * @throws \LogicException if two bundles share a common name + * @throws \LogicException if a bundle tries to extend a non-registered bundle + * @throws \LogicException if a bundle tries to extend itself + * @throws \LogicException if two bundles extend the same ancestor + */ + protected function initializeBundles() + { + // init bundles + $this->bundles = array(); + $topMostBundles = array(); + $directChildren = array(); + + foreach ($this->registerBundles() as $bundle) { + $name = $bundle->getName(); + if (isset($this->bundles[$name])) { + throw new \LogicException(sprintf('Trying to register two bundles with the same name "%s"', $name)); + } + $this->bundles[$name] = $bundle; + + if ($parentName = $bundle->getParent()) { + @trigger_error('Bundle inheritance is deprecated as of 3.4 and will be removed in 4.0.', E_USER_DEPRECATED); + + if (isset($directChildren[$parentName])) { + throw new \LogicException(sprintf('Bundle "%s" is directly extended by two bundles "%s" and "%s".', $parentName, $name, $directChildren[$parentName])); + } + if ($parentName == $name) { + throw new \LogicException(sprintf('Bundle "%s" can not extend itself.', $name)); + } + $directChildren[$parentName] = $name; + } else { + $topMostBundles[$name] = $bundle; + } + } + + // look for orphans + if (!empty($directChildren) && count($diff = array_diff_key($directChildren, $this->bundles))) { + $diff = array_keys($diff); + + throw new \LogicException(sprintf('Bundle "%s" extends bundle "%s", which is not registered.', $directChildren[$diff[0]], $diff[0])); + } + + // inheritance + $this->bundleMap = array(); + foreach ($topMostBundles as $name => $bundle) { + $bundleMap = array($bundle); + $hierarchy = array($name); + + while (isset($directChildren[$name])) { + $name = $directChildren[$name]; + array_unshift($bundleMap, $this->bundles[$name]); + $hierarchy[] = $name; + } + + foreach ($hierarchy as $hierarchyBundle) { + $this->bundleMap[$hierarchyBundle] = $bundleMap; + array_pop($bundleMap); + } + } + } + + /** + * The extension point similar to the Bundle::build() method. + * + * Use this method to register compiler passes and manipulate the container during the building process. + */ + protected function build(ContainerBuilder $container) + { + } + + /** + * Gets the container class. + * + * @return string The container class + */ + protected function getContainerClass() + { + return $this->name.ucfirst($this->environment).($this->debug ? 'Debug' : '').'ProjectContainer'; + } + + /** + * Gets the container's base class. + * + * All names except Container must be fully qualified. + * + * @return string + */ + protected function getContainerBaseClass() + { + return 'Container'; + } + + /** + * Initializes the service container. + * + * The cached version of the service container is used when fresh, otherwise the + * container is built. + */ + protected function initializeContainer() + { + $class = $this->getContainerClass(); + $cacheDir = $this->warmupDir ?: $this->getCacheDir(); + $cache = new ConfigCache($cacheDir.'/'.$class.'.php', $this->debug); + $oldContainer = null; + if ($fresh = $cache->isFresh()) { + // Silence E_WARNING to ignore "include" failures - don't use "@" to prevent silencing fatal errors + $errorLevel = error_reporting(\E_ALL ^ \E_WARNING); + $fresh = $oldContainer = false; + try { + if (\is_object($this->container = include $cache->getPath())) { + $this->container->set('kernel', $this); + $oldContainer = $this->container; + $fresh = true; + } + } catch (\Throwable $e) { + } catch (\Exception $e) { + } finally { + error_reporting($errorLevel); + } + } + + if ($fresh) { + return; + } + + if ($this->debug) { + $collectedLogs = array(); + $previousHandler = defined('PHPUNIT_COMPOSER_INSTALL'); + $previousHandler = $previousHandler ?: set_error_handler(function ($type, $message, $file, $line) use (&$collectedLogs, &$previousHandler) { + if (E_USER_DEPRECATED !== $type && E_DEPRECATED !== $type) { + return $previousHandler ? $previousHandler($type, $message, $file, $line) : false; + } + + if (isset($collectedLogs[$message])) { + ++$collectedLogs[$message]['count']; + + return; + } + + $backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 3); + // Clean the trace by removing first frames added by the error handler itself. + for ($i = 0; isset($backtrace[$i]); ++$i) { + if (isset($backtrace[$i]['file'], $backtrace[$i]['line']) && $backtrace[$i]['line'] === $line && $backtrace[$i]['file'] === $file) { + $backtrace = array_slice($backtrace, 1 + $i); + break; + } + } + + $collectedLogs[$message] = array( + 'type' => $type, + 'message' => $message, + 'file' => $file, + 'line' => $line, + 'trace' => $backtrace, + 'count' => 1, + ); + }); + } + + try { + $container = null; + $container = $this->buildContainer(); + $container->compile(); + } finally { + if ($this->debug && true !== $previousHandler) { + restore_error_handler(); + + file_put_contents($cacheDir.'/'.$class.'Deprecations.log', serialize(array_values($collectedLogs))); + file_put_contents($cacheDir.'/'.$class.'Compiler.log', null !== $container ? implode("\n", $container->getCompiler()->getLog()) : ''); + } + } + + if (null === $oldContainer) { + $errorLevel = error_reporting(\E_ALL ^ \E_WARNING); + try { + $oldContainer = include $cache->getPath(); + } catch (\Throwable $e) { + } catch (\Exception $e) { + } finally { + error_reporting($errorLevel); + } + } + $oldContainer = is_object($oldContainer) ? new \ReflectionClass($oldContainer) : false; + + $this->dumpContainer($cache, $container, $class, $this->getContainerBaseClass()); + $this->container = require $cache->getPath(); + $this->container->set('kernel', $this); + + if ($oldContainer && get_class($this->container) !== $oldContainer->name) { + // Because concurrent requests might still be using them, + // old container files are not removed immediately, + // but on a next dump of the container. + $oldContainerDir = dirname($oldContainer->getFileName()); + foreach (glob(dirname($oldContainerDir).'/*.legacy') as $legacyContainer) { + if ($oldContainerDir.'.legacy' !== $legacyContainer && @unlink($legacyContainer)) { + (new Filesystem())->remove(substr($legacyContainer, 0, -7)); + } + } + + touch($oldContainerDir.'.legacy'); + } + + if ($this->container->has('cache_warmer')) { + $this->container->get('cache_warmer')->warmUp($this->container->getParameter('kernel.cache_dir')); + } + } + + /** + * Returns the kernel parameters. + * + * @return array An array of kernel parameters + */ + protected function getKernelParameters() + { + $bundles = array(); + $bundlesMetadata = array(); + + foreach ($this->bundles as $name => $bundle) { + $bundles[$name] = get_class($bundle); + $bundlesMetadata[$name] = array( + 'parent' => $bundle->getParent(), + 'path' => $bundle->getPath(), + 'namespace' => $bundle->getNamespace(), + ); + } + + return array_merge( + array( + 'kernel.root_dir' => realpath($this->rootDir) ?: $this->rootDir, + 'kernel.project_dir' => realpath($this->getProjectDir()) ?: $this->getProjectDir(), + 'kernel.environment' => $this->environment, + 'kernel.debug' => $this->debug, + 'kernel.name' => $this->name, + 'kernel.cache_dir' => realpath($cacheDir = $this->warmupDir ?: $this->getCacheDir()) ?: $cacheDir, + 'kernel.logs_dir' => realpath($this->getLogDir()) ?: $this->getLogDir(), + 'kernel.bundles' => $bundles, + 'kernel.bundles_metadata' => $bundlesMetadata, + 'kernel.charset' => $this->getCharset(), + 'kernel.container_class' => $this->getContainerClass(), + ), + $this->getEnvParameters(false) + ); + } + + /** + * Gets the environment parameters. + * + * Only the parameters starting with "SYMFONY__" are considered. + * + * @return array An array of parameters + * + * @deprecated since version 3.3, to be removed in 4.0 + */ + protected function getEnvParameters() + { + if (0 === func_num_args() || func_get_arg(0)) { + @trigger_error(sprintf('The %s() method is deprecated as of 3.3 and will be removed in 4.0. Use the %%env()%% syntax to get the value of any environment variable from configuration files instead.', __METHOD__), E_USER_DEPRECATED); + } + + $parameters = array(); + foreach ($_SERVER as $key => $value) { + if (0 === strpos($key, 'SYMFONY__')) { + @trigger_error(sprintf('The support of special environment variables that start with SYMFONY__ (such as "%s") is deprecated as of 3.3 and will be removed in 4.0. Use the %%env()%% syntax instead to get the value of environment variables in configuration files.', $key), E_USER_DEPRECATED); + $parameters[strtolower(str_replace('__', '.', substr($key, 9)))] = $value; + } + } + + return $parameters; + } + + /** + * Builds the service container. + * + * @return ContainerBuilder The compiled service container + * + * @throws \RuntimeException + */ + protected function buildContainer() + { + foreach (array('cache' => $this->warmupDir ?: $this->getCacheDir(), 'logs' => $this->getLogDir()) as $name => $dir) { + if (!is_dir($dir)) { + if (false === @mkdir($dir, 0777, true) && !is_dir($dir)) { + throw new \RuntimeException(sprintf("Unable to create the %s directory (%s)\n", $name, $dir)); + } + } elseif (!is_writable($dir)) { + throw new \RuntimeException(sprintf("Unable to write in the %s directory (%s)\n", $name, $dir)); + } + } + + $container = $this->getContainerBuilder(); + $container->addObjectResource($this); + $this->prepareContainer($container); + + if (null !== $cont = $this->registerContainerConfiguration($this->getContainerLoader($container))) { + $container->merge($cont); + } + + $container->addCompilerPass(new AddAnnotatedClassesToCachePass($this)); + $container->addResource(new EnvParametersResource('SYMFONY__')); + + return $container; + } + + /** + * Prepares the ContainerBuilder before it is compiled. + */ + protected function prepareContainer(ContainerBuilder $container) + { + $extensions = array(); + foreach ($this->bundles as $bundle) { + if ($extension = $bundle->getContainerExtension()) { + $container->registerExtension($extension); + $extensions[] = $extension->getAlias(); + } + + if ($this->debug) { + $container->addObjectResource($bundle); + } + } + + foreach ($this->bundles as $bundle) { + $bundle->build($container); + } + + $this->build($container); + + // ensure these extensions are implicitly loaded + $container->getCompilerPassConfig()->setMergePass(new MergeExtensionConfigurationPass($extensions)); + } + + /** + * Gets a new ContainerBuilder instance used to build the service container. + * + * @return ContainerBuilder + */ + protected function getContainerBuilder() + { + $container = new ContainerBuilder(); + $container->getParameterBag()->add($this->getKernelParameters()); + + if ($this instanceof CompilerPassInterface) { + $container->addCompilerPass($this, PassConfig::TYPE_BEFORE_OPTIMIZATION, -10000); + } + if (class_exists('ProxyManager\Configuration') && class_exists('Symfony\Bridge\ProxyManager\LazyProxy\Instantiator\RuntimeInstantiator')) { + $container->setProxyInstantiator(new RuntimeInstantiator()); + } + + return $container; + } + + /** + * Dumps the service container to PHP code in the cache. + * + * @param ConfigCache $cache The config cache + * @param ContainerBuilder $container The service container + * @param string $class The name of the class to generate + * @param string $baseClass The name of the container's base class + */ + protected function dumpContainer(ConfigCache $cache, ContainerBuilder $container, $class, $baseClass) + { + // cache the container + $dumper = new PhpDumper($container); + + if (class_exists('ProxyManager\Configuration') && class_exists('Symfony\Bridge\ProxyManager\LazyProxy\PhpDumper\ProxyDumper')) { + $dumper->setProxyDumper(new ProxyDumper(substr(hash('sha256', $cache->getPath()), 0, 7))); + } + + $content = $dumper->dump(array( + 'class' => $class, + 'base_class' => $baseClass, + 'file' => $cache->getPath(), + 'as_files' => true, + 'debug' => $this->debug, + 'inline_class_loader_parameter' => \PHP_VERSION_ID >= 70000 && !$this->loadClassCache && !class_exists(ClassCollectionLoader::class, false) ? 'container.dumper.inline_class_loader' : null, + )); + + $rootCode = array_pop($content); + $dir = dirname($cache->getPath()).'/'; + $fs = new Filesystem(); + + foreach ($content as $file => $code) { + $fs->dumpFile($dir.$file, $code); + @chmod($dir.$file, 0666 & ~umask()); + } + @unlink(dirname($dir.$file).'.legacy'); + + $cache->write($rootCode, $container->getResources()); + } + + /** + * Returns a loader for the container. + * + * @return DelegatingLoader The loader + */ + protected function getContainerLoader(ContainerInterface $container) + { + $locator = new FileLocator($this); + $resolver = new LoaderResolver(array( + new XmlFileLoader($container, $locator), + new YamlFileLoader($container, $locator), + new IniFileLoader($container, $locator), + new PhpFileLoader($container, $locator), + new GlobFileLoader($locator), + new DirectoryLoader($container, $locator), + new ClosureLoader($container), + )); + + return new DelegatingLoader($resolver); + } + + /** + * Removes comments from a PHP source string. + * + * We don't use the PHP php_strip_whitespace() function + * as we want the content to be readable and well-formatted. + * + * @param string $source A PHP string + * + * @return string The PHP string with the comments removed + */ + public static function stripComments($source) + { + if (!function_exists('token_get_all')) { + return $source; + } + + $rawChunk = ''; + $output = ''; + $tokens = token_get_all($source); + $ignoreSpace = false; + for ($i = 0; isset($tokens[$i]); ++$i) { + $token = $tokens[$i]; + if (!isset($token[1]) || 'b"' === $token) { + $rawChunk .= $token; + } elseif (T_START_HEREDOC === $token[0]) { + $output .= $rawChunk.$token[1]; + do { + $token = $tokens[++$i]; + $output .= isset($token[1]) && 'b"' !== $token ? $token[1] : $token; + } while (T_END_HEREDOC !== $token[0]); + $rawChunk = ''; + } elseif (T_WHITESPACE === $token[0]) { + if ($ignoreSpace) { + $ignoreSpace = false; + + continue; + } + + // replace multiple new lines with a single newline + $rawChunk .= preg_replace(array('/\n{2,}/S'), "\n", $token[1]); + } elseif (in_array($token[0], array(T_COMMENT, T_DOC_COMMENT))) { + $ignoreSpace = true; + } else { + $rawChunk .= $token[1]; + + // The PHP-open tag already has a new-line + if (T_OPEN_TAG === $token[0]) { + $ignoreSpace = true; + } + } + } + + $output .= $rawChunk; + + if (\PHP_VERSION_ID >= 70000) { + // PHP 7 memory manager will not release after token_get_all(), see https://bugs.php.net/70098 + unset($tokens, $rawChunk); + gc_mem_caches(); + } + + return $output; + } + + public function serialize() + { + return serialize(array($this->environment, $this->debug)); + } + + public function unserialize($data) + { + if (\PHP_VERSION_ID >= 70000) { + list($environment, $debug) = unserialize($data, array('allowed_classes' => false)); + } else { + list($environment, $debug) = unserialize($data); + } + + $this->__construct($environment, $debug); + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/KernelEvents.php b/lib/silex/vendor/symfony/http-kernel/KernelEvents.php new file mode 100644 index 000000000..674376325 --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/KernelEvents.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\HttpKernel; + +/** + * Contains all events thrown in the HttpKernel component. + * + * @author Bernhard Schussek + */ +final class KernelEvents +{ + /** + * The REQUEST event occurs at the very beginning of request + * dispatching. + * + * This event allows you to create a response for a request before any + * other code in the framework is executed. + * + * @Event("Symfony\Component\HttpKernel\Event\GetResponseEvent") + */ + const REQUEST = 'kernel.request'; + + /** + * The EXCEPTION event occurs when an uncaught exception appears. + * + * This event allows you to create a response for a thrown exception or + * to modify the thrown exception. + * + * @Event("Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent") + */ + const EXCEPTION = 'kernel.exception'; + + /** + * The VIEW event occurs when the return value of a controller + * is not a Response instance. + * + * This event allows you to create a response for the return value of the + * controller. + * + * @Event("Symfony\Component\HttpKernel\Event\GetResponseForControllerResultEvent") + */ + const VIEW = 'kernel.view'; + + /** + * The CONTROLLER event occurs once a controller was found for + * handling a request. + * + * This event allows you to change the controller that will handle the + * request. + * + * @Event("Symfony\Component\HttpKernel\Event\FilterControllerEvent") + */ + const CONTROLLER = 'kernel.controller'; + + /** + * The CONTROLLER_ARGUMENTS event occurs once controller arguments have been resolved. + * + * This event allows you to change the arguments that will be passed to + * the controller. + * + * @Event("Symfony\Component\HttpKernel\Event\FilterControllerArgumentsEvent") + */ + const CONTROLLER_ARGUMENTS = 'kernel.controller_arguments'; + + /** + * The RESPONSE event occurs once a response was created for + * replying to a request. + * + * This event allows you to modify or replace the response that will be + * replied. + * + * @Event("Symfony\Component\HttpKernel\Event\FilterResponseEvent") + */ + const RESPONSE = 'kernel.response'; + + /** + * The TERMINATE event occurs once a response was sent. + * + * This event allows you to run expensive post-response jobs. + * + * @Event("Symfony\Component\HttpKernel\Event\PostResponseEvent") + */ + const TERMINATE = 'kernel.terminate'; + + /** + * The FINISH_REQUEST event occurs when a response was generated for a request. + * + * This event allows you to reset the global and environmental state of + * the application, when it was changed during the request. + * + * @Event("Symfony\Component\HttpKernel\Event\FinishRequestEvent") + */ + const FINISH_REQUEST = 'kernel.finish_request'; +} diff --git a/lib/silex/vendor/symfony/http-kernel/KernelInterface.php b/lib/silex/vendor/symfony/http-kernel/KernelInterface.php new file mode 100644 index 000000000..b341e3e9f --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/KernelInterface.php @@ -0,0 +1,165 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel; + +use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\HttpKernel\Bundle\BundleInterface; +use Symfony\Component\Config\Loader\LoaderInterface; + +/** + * The Kernel is the heart of the Symfony system. + * + * It manages an environment made of bundles. + * + * @author Fabien Potencier + */ +interface KernelInterface extends HttpKernelInterface, \Serializable +{ + /** + * Returns an array of bundles to register. + * + * @return BundleInterface[] An array of bundle instances + */ + public function registerBundles(); + + /** + * Loads the container configuration. + */ + public function registerContainerConfiguration(LoaderInterface $loader); + + /** + * Boots the current kernel. + */ + public function boot(); + + /** + * Shutdowns the kernel. + * + * This method is mainly useful when doing functional testing. + */ + public function shutdown(); + + /** + * Gets the registered bundle instances. + * + * @return BundleInterface[] An array of registered bundle instances + */ + public function getBundles(); + + /** + * Returns a bundle and optionally its descendants by its name. + * + * The second argument is deprecated as of 3.4 and will be removed in 4.0. This method + * will always return an instance of BundleInterface in 4.0. + * + * @param string $name Bundle name + * @param bool $first Whether to return the first bundle only or together with its descendants + * + * @return BundleInterface|BundleInterface[] A BundleInterface instance or an array of BundleInterface instances if $first is false + * + * @throws \InvalidArgumentException when the bundle is not enabled + */ + public function getBundle($name, $first = true); + + /** + * Returns the file path for a given resource. + * + * A Resource can be a file or a directory. + * + * The resource name must follow the following pattern: + * + * "@BundleName/path/to/a/file.something" + * + * where BundleName is the name of the bundle + * and the remaining part is the relative path in the bundle. + * + * If $dir is passed, and the first segment of the path is "Resources", + * this method will look for a file named: + * + * $dir//path/without/Resources + * + * before looking in the bundle resource folder. + * + * @param string $name A resource name to locate + * @param string $dir A directory where to look for the resource first + * @param bool $first Whether to return the first path or paths for all matching bundles + * + * @return string|array The absolute path of the resource or an array if $first is false + * + * @throws \InvalidArgumentException if the file cannot be found or the name is not valid + * @throws \RuntimeException if the name contains invalid/unsafe characters + */ + public function locateResource($name, $dir = null, $first = true); + + /** + * Gets the name of the kernel. + * + * @return string The kernel name + */ + public function getName(); + + /** + * Gets the environment. + * + * @return string The current environment + */ + public function getEnvironment(); + + /** + * Checks if debug mode is enabled. + * + * @return bool true if debug mode is enabled, false otherwise + */ + public function isDebug(); + + /** + * Gets the application root dir (path of the project's Kernel class). + * + * @return string The Kernel root dir + */ + public function getRootDir(); + + /** + * Gets the current container. + * + * @return ContainerInterface A ContainerInterface instance + */ + public function getContainer(); + + /** + * Gets the request start time (not available if debug is disabled). + * + * @return int The request start timestamp + */ + public function getStartTime(); + + /** + * Gets the cache directory. + * + * @return string The cache directory + */ + public function getCacheDir(); + + /** + * Gets the log directory. + * + * @return string The log directory + */ + public function getLogDir(); + + /** + * Gets the charset of the application. + * + * @return string The charset + */ + public function getCharset(); +} diff --git a/lib/silex/vendor/symfony/http-kernel/LICENSE b/lib/silex/vendor/symfony/http-kernel/LICENSE new file mode 100644 index 000000000..21d7fb9e2 --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2004-2018 Fabien Potencier + +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/lib/silex/vendor/symfony/http-kernel/Log/DebugLoggerInterface.php b/lib/silex/vendor/symfony/http-kernel/Log/DebugLoggerInterface.php new file mode 100644 index 000000000..f0606d3b0 --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Log/DebugLoggerInterface.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\HttpKernel\Log; + +/** + * DebugLoggerInterface. + * + * @author Fabien Potencier + * + * @method clear() Removes all log records. + */ +interface DebugLoggerInterface +{ + /** + * Returns an array of logs. + * + * A log is an array with the following mandatory keys: + * timestamp, message, priority, and priorityName. + * It can also have an optional context key containing an array. + * + * @return array An array of logs + */ + public function getLogs(); + + /** + * Returns the number of errors. + * + * @return int The number of errors + */ + public function countErrors(); +} diff --git a/lib/silex/vendor/symfony/http-kernel/Log/Logger.php b/lib/silex/vendor/symfony/http-kernel/Log/Logger.php new file mode 100644 index 000000000..617efcf13 --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Log/Logger.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\HttpKernel\Log; + +use Psr\Log\AbstractLogger; +use Psr\Log\InvalidArgumentException; +use Psr\Log\LogLevel; + +/** + * Minimalist PSR-3 logger designed to write in stderr or any other stream. + * + * @author Kévin Dunglas + */ +class Logger extends AbstractLogger +{ + private static $levels = array( + LogLevel::DEBUG => 0, + LogLevel::INFO => 1, + LogLevel::NOTICE => 2, + LogLevel::WARNING => 3, + LogLevel::ERROR => 4, + LogLevel::CRITICAL => 5, + LogLevel::ALERT => 6, + LogLevel::EMERGENCY => 7, + ); + + private $minLevelIndex; + private $formatter; + private $handle; + + public function __construct($minLevel = null, $output = 'php://stderr', callable $formatter = null) + { + if (null === $minLevel) { + $minLevel = LogLevel::WARNING; + + if (isset($_ENV['SHELL_VERBOSITY']) || isset($_SERVER['SHELL_VERBOSITY'])) { + switch ((int) (isset($_ENV['SHELL_VERBOSITY']) ? $_ENV['SHELL_VERBOSITY'] : $_SERVER['SHELL_VERBOSITY'])) { + case -1: $minLevel = LogLevel::ERROR; break; + case 1: $minLevel = LogLevel::NOTICE; break; + case 2: $minLevel = LogLevel::INFO; break; + case 3: $minLevel = LogLevel::DEBUG; break; + } + } + } + + if (!isset(self::$levels[$minLevel])) { + throw new InvalidArgumentException(sprintf('The log level "%s" does not exist.', $minLevel)); + } + + $this->minLevelIndex = self::$levels[$minLevel]; + $this->formatter = $formatter ?: array($this, 'format'); + if (false === $this->handle = is_resource($output) ? $output : @fopen($output, 'a')) { + throw new InvalidArgumentException(sprintf('Unable to open "%s".', $output)); + } + } + + /** + * {@inheritdoc} + */ + public function log($level, $message, array $context = array()) + { + if (!isset(self::$levels[$level])) { + throw new InvalidArgumentException(sprintf('The log level "%s" does not exist.', $level)); + } + + if (self::$levels[$level] < $this->minLevelIndex) { + return; + } + + $formatter = $this->formatter; + fwrite($this->handle, $formatter($level, $message, $context)); + } + + /** + * @param string $level + * @param string $message + * @param array $context + * + * @return string + */ + private function format($level, $message, array $context) + { + if (false !== strpos($message, '{')) { + $replacements = array(); + foreach ($context as $key => $val) { + if (null === $val || is_scalar($val) || (\is_object($val) && method_exists($val, '__toString'))) { + $replacements["{{$key}}"] = $val; + } elseif ($val instanceof \DateTimeInterface) { + $replacements["{{$key}}"] = $val->format(\DateTime::RFC3339); + } elseif (\is_object($val)) { + $replacements["{{$key}}"] = '[object '.\get_class($val).']'; + } else { + $replacements["{{$key}}"] = '['.\gettype($val).']'; + } + } + + $message = strtr($message, $replacements); + } + + return sprintf('%s [%s] %s', date(\DateTime::RFC3339), $level, $message).\PHP_EOL; + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/Profiler/FileProfilerStorage.php b/lib/silex/vendor/symfony/http-kernel/Profiler/FileProfilerStorage.php new file mode 100644 index 000000000..e24b2e018 --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Profiler/FileProfilerStorage.php @@ -0,0 +1,292 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Profiler; + +/** + * Storage for profiler using files. + * + * @author Alexandre Salomé + */ +class FileProfilerStorage implements ProfilerStorageInterface +{ + /** + * Folder where profiler data are stored. + * + * @var string + */ + private $folder; + + /** + * Constructs the file storage using a "dsn-like" path. + * + * Example : "file:/path/to/the/storage/folder" + * + * @param string $dsn The DSN + * + * @throws \RuntimeException + */ + public function __construct($dsn) + { + if (0 !== strpos($dsn, 'file:')) { + throw new \RuntimeException(sprintf('Please check your configuration. You are trying to use FileStorage with an invalid dsn "%s". The expected format is "file:/path/to/the/storage/folder".', $dsn)); + } + $this->folder = substr($dsn, 5); + + if (!is_dir($this->folder) && false === @mkdir($this->folder, 0777, true) && !is_dir($this->folder)) { + throw new \RuntimeException(sprintf('Unable to create the storage directory (%s).', $this->folder)); + } + } + + /** + * {@inheritdoc} + */ + public function find($ip, $url, $limit, $method, $start = null, $end = null, $statusCode = null) + { + $file = $this->getIndexFilename(); + + if (!file_exists($file)) { + return array(); + } + + $file = fopen($file, 'r'); + fseek($file, 0, SEEK_END); + + $result = array(); + while (count($result) < $limit && $line = $this->readLineFromFile($file)) { + $values = str_getcsv($line); + list($csvToken, $csvIp, $csvMethod, $csvUrl, $csvTime, $csvParent, $csvStatusCode) = $values; + $csvTime = (int) $csvTime; + + if ($ip && false === strpos($csvIp, $ip) || $url && false === strpos($csvUrl, $url) || $method && false === strpos($csvMethod, $method) || $statusCode && false === strpos($csvStatusCode, $statusCode)) { + continue; + } + + if (!empty($start) && $csvTime < $start) { + continue; + } + + if (!empty($end) && $csvTime > $end) { + continue; + } + + $result[$csvToken] = array( + 'token' => $csvToken, + 'ip' => $csvIp, + 'method' => $csvMethod, + 'url' => $csvUrl, + 'time' => $csvTime, + 'parent' => $csvParent, + 'status_code' => $csvStatusCode, + ); + } + + fclose($file); + + return array_values($result); + } + + /** + * {@inheritdoc} + */ + public function purge() + { + $flags = \FilesystemIterator::SKIP_DOTS; + $iterator = new \RecursiveDirectoryIterator($this->folder, $flags); + $iterator = new \RecursiveIteratorIterator($iterator, \RecursiveIteratorIterator::CHILD_FIRST); + + foreach ($iterator as $file) { + if (is_file($file)) { + unlink($file); + } else { + rmdir($file); + } + } + } + + /** + * {@inheritdoc} + */ + public function read($token) + { + if (!$token || !file_exists($file = $this->getFilename($token))) { + return; + } + + return $this->createProfileFromData($token, unserialize(file_get_contents($file))); + } + + /** + * {@inheritdoc} + * + * @throws \RuntimeException + */ + public function write(Profile $profile) + { + $file = $this->getFilename($profile->getToken()); + + $profileIndexed = is_file($file); + if (!$profileIndexed) { + // Create directory + $dir = dirname($file); + if (!is_dir($dir) && false === @mkdir($dir, 0777, true) && !is_dir($dir)) { + throw new \RuntimeException(sprintf('Unable to create the storage directory (%s).', $dir)); + } + } + + $profileToken = $profile->getToken(); + // when there are errors in sub-requests, the parent and/or children tokens + // may equal the profile token, resulting in infinite loops + $parentToken = $profile->getParentToken() !== $profileToken ? $profile->getParentToken() : null; + $childrenToken = array_filter(array_map(function ($p) use ($profileToken) { + return $profileToken !== $p->getToken() ? $p->getToken() : null; + }, $profile->getChildren())); + + // Store profile + $data = array( + 'token' => $profileToken, + 'parent' => $parentToken, + 'children' => $childrenToken, + 'data' => $profile->getCollectors(), + 'ip' => $profile->getIp(), + 'method' => $profile->getMethod(), + 'url' => $profile->getUrl(), + 'time' => $profile->getTime(), + 'status_code' => $profile->getStatusCode(), + ); + + if (false === file_put_contents($file, serialize($data))) { + return false; + } + + if (!$profileIndexed) { + // Add to index + if (false === $file = fopen($this->getIndexFilename(), 'a')) { + return false; + } + + fputcsv($file, array( + $profile->getToken(), + $profile->getIp(), + $profile->getMethod(), + $profile->getUrl(), + $profile->getTime(), + $profile->getParentToken(), + $profile->getStatusCode(), + )); + fclose($file); + } + + return true; + } + + /** + * Gets filename to store data, associated to the token. + * + * @param string $token + * + * @return string The profile filename + */ + protected function getFilename($token) + { + // Uses 4 last characters, because first are mostly the same. + $folderA = substr($token, -2, 2); + $folderB = substr($token, -4, 2); + + return $this->folder.'/'.$folderA.'/'.$folderB.'/'.$token; + } + + /** + * Gets the index filename. + * + * @return string The index filename + */ + protected function getIndexFilename() + { + return $this->folder.'/index.csv'; + } + + /** + * Reads a line in the file, backward. + * + * This function automatically skips the empty lines and do not include the line return in result value. + * + * @param resource $file The file resource, with the pointer placed at the end of the line to read + * + * @return mixed A string representing the line or null if beginning of file is reached + */ + protected function readLineFromFile($file) + { + $line = ''; + $position = ftell($file); + + if (0 === $position) { + return; + } + + while (true) { + $chunkSize = min($position, 1024); + $position -= $chunkSize; + fseek($file, $position); + + if (0 === $chunkSize) { + // bof reached + break; + } + + $buffer = fread($file, $chunkSize); + + if (false === ($upTo = strrpos($buffer, "\n"))) { + $line = $buffer.$line; + continue; + } + + $position += $upTo; + $line = substr($buffer, $upTo + 1).$line; + fseek($file, max(0, $position), SEEK_SET); + + if ('' !== $line) { + break; + } + } + + return '' === $line ? null : $line; + } + + protected function createProfileFromData($token, $data, $parent = null) + { + $profile = new Profile($token); + $profile->setIp($data['ip']); + $profile->setMethod($data['method']); + $profile->setUrl($data['url']); + $profile->setTime($data['time']); + $profile->setStatusCode($data['status_code']); + $profile->setCollectors($data['data']); + + if (!$parent && $data['parent']) { + $parent = $this->read($data['parent']); + } + + if ($parent) { + $profile->setParent($parent); + } + + foreach ($data['children'] as $token) { + if (!$token || !file_exists($file = $this->getFilename($token))) { + continue; + } + + $profile->addChild($this->createProfileFromData($token, unserialize(file_get_contents($file)), $profile)); + } + + return $profile; + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/Profiler/Profile.php b/lib/silex/vendor/symfony/http-kernel/Profiler/Profile.php new file mode 100644 index 000000000..c21c9d38a --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Profiler/Profile.php @@ -0,0 +1,287 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Profiler; + +use Symfony\Component\HttpKernel\DataCollector\DataCollectorInterface; + +/** + * Profile. + * + * @author Fabien Potencier + */ +class Profile +{ + private $token; + + /** + * @var DataCollectorInterface[] + */ + private $collectors = array(); + + private $ip; + private $method; + private $url; + private $time; + private $statusCode; + + /** + * @var Profile + */ + private $parent; + + /** + * @var Profile[] + */ + private $children = array(); + + /** + * @param string $token The token + */ + public function __construct($token) + { + $this->token = $token; + } + + /** + * Sets the token. + * + * @param string $token The token + */ + public function setToken($token) + { + $this->token = $token; + } + + /** + * Gets the token. + * + * @return string The token + */ + public function getToken() + { + return $this->token; + } + + /** + * Sets the parent token. + */ + public function setParent(Profile $parent) + { + $this->parent = $parent; + } + + /** + * Returns the parent profile. + * + * @return self + */ + public function getParent() + { + return $this->parent; + } + + /** + * Returns the parent token. + * + * @return null|string The parent token + */ + public function getParentToken() + { + return $this->parent ? $this->parent->getToken() : null; + } + + /** + * Returns the IP. + * + * @return string The IP + */ + public function getIp() + { + return $this->ip; + } + + /** + * Sets the IP. + * + * @param string $ip + */ + public function setIp($ip) + { + $this->ip = $ip; + } + + /** + * Returns the request method. + * + * @return string The request method + */ + public function getMethod() + { + return $this->method; + } + + public function setMethod($method) + { + $this->method = $method; + } + + /** + * Returns the URL. + * + * @return string The URL + */ + public function getUrl() + { + return $this->url; + } + + public function setUrl($url) + { + $this->url = $url; + } + + /** + * Returns the time. + * + * @return int The time + */ + public function getTime() + { + if (null === $this->time) { + return 0; + } + + return $this->time; + } + + /** + * @param int $time The time + */ + public function setTime($time) + { + $this->time = $time; + } + + /** + * @param int $statusCode + */ + public function setStatusCode($statusCode) + { + $this->statusCode = $statusCode; + } + + /** + * @return int + */ + public function getStatusCode() + { + return $this->statusCode; + } + + /** + * Finds children profilers. + * + * @return self[] + */ + public function getChildren() + { + return $this->children; + } + + /** + * Sets children profiler. + * + * @param Profile[] $children + */ + public function setChildren(array $children) + { + $this->children = array(); + foreach ($children as $child) { + $this->addChild($child); + } + } + + /** + * Adds the child token. + */ + public function addChild(Profile $child) + { + $this->children[] = $child; + $child->setParent($this); + } + + /** + * Gets a Collector by name. + * + * @param string $name A collector name + * + * @return DataCollectorInterface A DataCollectorInterface instance + * + * @throws \InvalidArgumentException if the collector does not exist + */ + public function getCollector($name) + { + if (!isset($this->collectors[$name])) { + throw new \InvalidArgumentException(sprintf('Collector "%s" does not exist.', $name)); + } + + return $this->collectors[$name]; + } + + /** + * Gets the Collectors associated with this profile. + * + * @return DataCollectorInterface[] + */ + public function getCollectors() + { + return $this->collectors; + } + + /** + * Sets the Collectors associated with this profile. + * + * @param DataCollectorInterface[] $collectors + */ + public function setCollectors(array $collectors) + { + $this->collectors = array(); + foreach ($collectors as $collector) { + $this->addCollector($collector); + } + } + + /** + * Adds a Collector. + */ + public function addCollector(DataCollectorInterface $collector) + { + $this->collectors[$collector->getName()] = $collector; + } + + /** + * Returns true if a Collector for the given name exists. + * + * @param string $name A collector name + * + * @return bool + */ + public function hasCollector($name) + { + return isset($this->collectors[$name]); + } + + public function __sleep() + { + return array('token', 'parent', 'children', 'collectors', 'ip', 'method', 'url', 'time', 'statusCode'); + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/Profiler/Profiler.php b/lib/silex/vendor/symfony/http-kernel/Profiler/Profiler.php new file mode 100644 index 000000000..15bfbc5a0 --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Profiler/Profiler.php @@ -0,0 +1,265 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Profiler; + +use Symfony\Component\HttpFoundation\Exception\ConflictingHeadersException; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\DataCollector\DataCollectorInterface; +use Symfony\Component\HttpKernel\DataCollector\LateDataCollectorInterface; +use Psr\Log\LoggerInterface; + +/** + * Profiler. + * + * @author Fabien Potencier + */ +class Profiler +{ + private $storage; + + /** + * @var DataCollectorInterface[] + */ + private $collectors = array(); + + private $logger; + private $initiallyEnabled = true; + private $enabled = true; + + /** + * @param bool $enable The initial enabled state + */ + public function __construct(ProfilerStorageInterface $storage, LoggerInterface $logger = null, $enable = true) + { + $this->storage = $storage; + $this->logger = $logger; + $this->initiallyEnabled = $this->enabled = (bool) $enable; + } + + /** + * Disables the profiler. + */ + public function disable() + { + $this->enabled = false; + } + + /** + * Enables the profiler. + */ + public function enable() + { + $this->enabled = true; + } + + /** + * Loads the Profile for the given Response. + * + * @return Profile|false A Profile instance + */ + public function loadProfileFromResponse(Response $response) + { + if (!$token = $response->headers->get('X-Debug-Token')) { + return false; + } + + return $this->loadProfile($token); + } + + /** + * Loads the Profile for the given token. + * + * @param string $token A token + * + * @return Profile A Profile instance + */ + public function loadProfile($token) + { + return $this->storage->read($token); + } + + /** + * Saves a Profile. + * + * @return bool + */ + public function saveProfile(Profile $profile) + { + // late collect + foreach ($profile->getCollectors() as $collector) { + if ($collector instanceof LateDataCollectorInterface) { + $collector->lateCollect(); + } + } + + if (!($ret = $this->storage->write($profile)) && null !== $this->logger) { + $this->logger->warning('Unable to store the profiler information.', array('configured_storage' => get_class($this->storage))); + } + + return $ret; + } + + /** + * Purges all data from the storage. + */ + public function purge() + { + $this->storage->purge(); + } + + /** + * Finds profiler tokens for the given criteria. + * + * @param string $ip The IP + * @param string $url The URL + * @param string $limit The maximum number of tokens to return + * @param string $method The request method + * @param string $start The start date to search from + * @param string $end The end date to search to + * @param string $statusCode The request status code + * + * @return array An array of tokens + * + * @see http://php.net/manual/en/datetime.formats.php for the supported date/time formats + */ + public function find($ip, $url, $limit, $method, $start, $end, $statusCode = null) + { + return $this->storage->find($ip, $url, $limit, $method, $this->getTimestamp($start), $this->getTimestamp($end), $statusCode); + } + + /** + * Collects data for the given Response. + * + * @return Profile|null A Profile instance or null if the profiler is disabled + */ + public function collect(Request $request, Response $response, \Exception $exception = null) + { + if (false === $this->enabled) { + return; + } + + $profile = new Profile(substr(hash('sha256', uniqid(mt_rand(), true)), 0, 6)); + $profile->setTime(time()); + $profile->setUrl($request->getUri()); + $profile->setMethod($request->getMethod()); + $profile->setStatusCode($response->getStatusCode()); + try { + $profile->setIp($request->getClientIp()); + } catch (ConflictingHeadersException $e) { + $profile->setIp('Unknown'); + } + + $response->headers->set('X-Debug-Token', $profile->getToken()); + + foreach ($this->collectors as $collector) { + $collector->collect($request, $response, $exception); + + // we need to clone for sub-requests + $profile->addCollector(clone $collector); + } + + return $profile; + } + + public function reset() + { + foreach ($this->collectors as $collector) { + if (!method_exists($collector, 'reset')) { + continue; + } + + $collector->reset(); + } + $this->enabled = $this->initiallyEnabled; + } + + /** + * Gets the Collectors associated with this profiler. + * + * @return array An array of collectors + */ + public function all() + { + return $this->collectors; + } + + /** + * Sets the Collectors associated with this profiler. + * + * @param DataCollectorInterface[] $collectors An array of collectors + */ + public function set(array $collectors = array()) + { + $this->collectors = array(); + foreach ($collectors as $collector) { + $this->add($collector); + } + } + + /** + * Adds a Collector. + */ + public function add(DataCollectorInterface $collector) + { + if (!method_exists($collector, 'reset')) { + @trigger_error(sprintf('Implementing "%s" without the "reset()" method is deprecated since Symfony 3.4 and will be unsupported in 4.0 for class "%s".', DataCollectorInterface::class, \get_class($collector)), E_USER_DEPRECATED); + } + + $this->collectors[$collector->getName()] = $collector; + } + + /** + * Returns true if a Collector for the given name exists. + * + * @param string $name A collector name + * + * @return bool + */ + public function has($name) + { + return isset($this->collectors[$name]); + } + + /** + * Gets a Collector by name. + * + * @param string $name A collector name + * + * @return DataCollectorInterface A DataCollectorInterface instance + * + * @throws \InvalidArgumentException if the collector does not exist + */ + public function get($name) + { + if (!isset($this->collectors[$name])) { + throw new \InvalidArgumentException(sprintf('Collector "%s" does not exist.', $name)); + } + + return $this->collectors[$name]; + } + + private function getTimestamp($value) + { + if (null === $value || '' == $value) { + return; + } + + try { + $value = new \DateTime(is_numeric($value) ? '@'.$value : $value); + } catch (\Exception $e) { + return; + } + + return $value->getTimestamp(); + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/Profiler/ProfilerStorageInterface.php b/lib/silex/vendor/symfony/http-kernel/Profiler/ProfilerStorageInterface.php new file mode 100644 index 000000000..544fb1fef --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Profiler/ProfilerStorageInterface.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\HttpKernel\Profiler; + +/** + * ProfilerStorageInterface. + * + * @author Fabien Potencier + */ +interface ProfilerStorageInterface +{ + /** + * Finds profiler tokens for the given criteria. + * + * @param string $ip The IP + * @param string $url The URL + * @param string $limit The maximum number of tokens to return + * @param string $method The request method + * @param int|null $start The start date to search from + * @param int|null $end The end date to search to + * + * @return array An array of tokens + */ + public function find($ip, $url, $limit, $method, $start = null, $end = null); + + /** + * Reads data associated with the given token. + * + * The method returns false if the token does not exist in the storage. + * + * @param string $token A token + * + * @return Profile The profile associated with token + */ + public function read($token); + + /** + * Saves a Profile. + * + * @return bool Write operation successful + */ + public function write(Profile $profile); + + /** + * Purges all data from the database. + */ + public function purge(); +} diff --git a/lib/silex/vendor/symfony/http-kernel/README.md b/lib/silex/vendor/symfony/http-kernel/README.md new file mode 100644 index 000000000..cc5e74b6b --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/README.md @@ -0,0 +1,16 @@ +HttpKernel Component +==================== + +The HttpKernel component provides a structured process for converting a Request +into a Response by making use of the EventDispatcher component. It's flexible +enough to create a full-stack framework (Symfony), a micro-framework (Silex) or +an advanced CMS system (Drupal). + +Resources +--------- + + * [Documentation](https://symfony.com/doc/current/components/http_kernel/index.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/lib/silex/vendor/symfony/http-kernel/RebootableInterface.php b/lib/silex/vendor/symfony/http-kernel/RebootableInterface.php new file mode 100644 index 000000000..58d9ef59e --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/RebootableInterface.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\HttpKernel; + +/** + * Allows the Kernel to be rebooted using a temporary cache directory. + * + * @author Nicolas Grekas + */ +interface RebootableInterface +{ + /** + * Reboots a kernel. + * + * The getCacheDir() method of a rebootable kernel should not be called + * while building the container. Use the %kernel.cache_dir% parameter instead. + * + * @param string|null $warmupDir pass null to reboot in the regular cache directory + */ + public function reboot($warmupDir); +} diff --git a/lib/silex/vendor/symfony/http-kernel/Resources/welcome.html.php b/lib/silex/vendor/symfony/http-kernel/Resources/welcome.html.php new file mode 100644 index 000000000..d8c37beb5 --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Resources/welcome.html.php @@ -0,0 +1,84 @@ + + + + + Welcome! + + + +
+
+
+

Welcome to Symfony

+
+ +
+

+ + + Your application is now ready. You can start working on it at:
+ +

+
+ + +
+
+

+ You're seeing this message because you have debug mode enabled and you haven't configured any URLs. +

+
+
+ + diff --git a/lib/silex/vendor/symfony/http-kernel/TerminableInterface.php b/lib/silex/vendor/symfony/http-kernel/TerminableInterface.php new file mode 100644 index 000000000..8aa331979 --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/TerminableInterface.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\HttpKernel; + +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +/** + * Terminable extends the Kernel request/response cycle with dispatching a post + * response event after sending the response and before shutting down the kernel. + * + * @author Jordi Boggiano + * @author Pierre Minnieur + */ +interface TerminableInterface +{ + /** + * Terminates a request/response cycle. + * + * Should be called after sending the response and before shutting down the kernel. + */ + public function terminate(Request $request, Response $response); +} diff --git a/lib/silex/vendor/symfony/http-kernel/Tests/Bundle/BundleTest.php b/lib/silex/vendor/symfony/http-kernel/Tests/Bundle/BundleTest.php new file mode 100644 index 000000000..8e52b097d --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Tests/Bundle/BundleTest.php @@ -0,0 +1,104 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Tests\Bundle; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\HttpKernel\Bundle\Bundle; +use Symfony\Component\HttpKernel\Tests\Fixtures\ExtensionNotValidBundle\ExtensionNotValidBundle; +use Symfony\Component\HttpKernel\Tests\Fixtures\ExtensionPresentBundle\ExtensionPresentBundle; +use Symfony\Component\HttpKernel\Tests\Fixtures\ExtensionAbsentBundle\ExtensionAbsentBundle; +use Symfony\Component\HttpKernel\Tests\Fixtures\ExtensionPresentBundle\Command\FooCommand; + +class BundleTest extends TestCase +{ + public function testGetContainerExtension() + { + $bundle = new ExtensionPresentBundle(); + + $this->assertInstanceOf( + 'Symfony\Component\HttpKernel\Tests\Fixtures\ExtensionPresentBundle\DependencyInjection\ExtensionPresentExtension', + $bundle->getContainerExtension() + ); + } + + /** + * @group legacy + * @expectedDeprecation Auto-registration of the command "Symfony\Component\HttpKernel\Tests\Fixtures\ExtensionPresentBundle\Command\FooCommand" is deprecated since Symfony 3.4 and won't be supported in 4.0. Use PSR-4 based service discovery instead. + */ + public function testRegisterCommands() + { + $cmd = new FooCommand(); + $app = $this->getMockBuilder('Symfony\Component\Console\Application')->getMock(); + $app->expects($this->once())->method('add')->with($this->equalTo($cmd)); + + $bundle = new ExtensionPresentBundle(); + $bundle->registerCommands($app); + + $bundle2 = new ExtensionAbsentBundle(); + + $this->assertNull($bundle2->registerCommands($app)); + } + + /** + * @expectedException \LogicException + * @expectedExceptionMessage must implement Symfony\Component\DependencyInjection\Extension\ExtensionInterface + */ + public function testGetContainerExtensionWithInvalidClass() + { + $bundle = new ExtensionNotValidBundle(); + $bundle->getContainerExtension(); + } + + public function testHttpKernelRegisterCommandsIgnoresCommandsThatAreRegisteredAsServices() + { + $container = new ContainerBuilder(); + $container->register('console.command.symfony_component_httpkernel_tests_fixtures_extensionpresentbundle_command_foocommand', 'Symfony\Component\HttpKernel\Tests\Fixtures\ExtensionPresentBundle\Command\FooCommand'); + + $application = $this->getMockBuilder('Symfony\Component\Console\Application')->getMock(); + // add() is never called when the found command classes are already registered as services + $application->expects($this->never())->method('add'); + + $bundle = new ExtensionPresentBundle(); + $bundle->setContainer($container); + $bundle->registerCommands($application); + } + + public function testBundleNameIsGuessedFromClass() + { + $bundle = new GuessedNameBundle(); + + $this->assertSame('Symfony\Component\HttpKernel\Tests\Bundle', $bundle->getNamespace()); + $this->assertSame('GuessedNameBundle', $bundle->getName()); + } + + public function testBundleNameCanBeExplicitlyProvided() + { + $bundle = new NamedBundle(); + + $this->assertSame('ExplicitlyNamedBundle', $bundle->getName()); + $this->assertSame('Symfony\Component\HttpKernel\Tests\Bundle', $bundle->getNamespace()); + $this->assertSame('ExplicitlyNamedBundle', $bundle->getName()); + } +} + +class NamedBundle extends Bundle +{ + public function __construct() + { + $this->name = 'ExplicitlyNamedBundle'; + } +} + +class GuessedNameBundle extends Bundle +{ +} diff --git a/lib/silex/vendor/symfony/http-kernel/Tests/CacheClearer/ChainCacheClearerTest.php b/lib/silex/vendor/symfony/http-kernel/Tests/CacheClearer/ChainCacheClearerTest.php new file mode 100644 index 000000000..ec2ecff94 --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Tests/CacheClearer/ChainCacheClearerTest.php @@ -0,0 +1,61 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Tests\CacheClearer; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\HttpKernel\CacheClearer\ChainCacheClearer; + +class ChainCacheClearerTest extends TestCase +{ + protected static $cacheDir; + + public static function setUpBeforeClass() + { + self::$cacheDir = tempnam(sys_get_temp_dir(), 'sf2_cache_clearer_dir'); + } + + public static function tearDownAfterClass() + { + @unlink(self::$cacheDir); + } + + public function testInjectClearersInConstructor() + { + $clearer = $this->getMockClearer(); + $clearer + ->expects($this->once()) + ->method('clear'); + + $chainClearer = new ChainCacheClearer(array($clearer)); + $chainClearer->clear(self::$cacheDir); + } + + /** + * @group legacy + */ + public function testInjectClearerUsingAdd() + { + $clearer = $this->getMockClearer(); + $clearer + ->expects($this->once()) + ->method('clear'); + + $chainClearer = new ChainCacheClearer(); + $chainClearer->add($clearer); + $chainClearer->clear(self::$cacheDir); + } + + protected function getMockClearer() + { + return $this->getMockBuilder('Symfony\Component\HttpKernel\CacheClearer\CacheClearerInterface')->getMock(); + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/Tests/CacheClearer/Psr6CacheClearerTest.php b/lib/silex/vendor/symfony/http-kernel/Tests/CacheClearer/Psr6CacheClearerTest.php new file mode 100644 index 000000000..3e20efbf1 --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Tests/CacheClearer/Psr6CacheClearerTest.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\HttpKernel\Tests\CacheClearer; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\HttpKernel\CacheClearer\Psr6CacheClearer; +use Psr\Cache\CacheItemPoolInterface; + +class Psr6CacheClearerTest extends TestCase +{ + public function testClearPoolsInjectedInConstructor() + { + $pool = $this->getMockBuilder(CacheItemPoolInterface::class)->getMock(); + $pool + ->expects($this->once()) + ->method('clear'); + + (new Psr6CacheClearer(array('pool' => $pool)))->clear(''); + } + + public function testClearPool() + { + $pool = $this->getMockBuilder(CacheItemPoolInterface::class)->getMock(); + $pool + ->expects($this->once()) + ->method('clear'); + + (new Psr6CacheClearer(array('pool' => $pool)))->clearPool('pool'); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage Cache pool not found: unknown + */ + public function testClearPoolThrowsExceptionOnUnreferencedPool() + { + (new Psr6CacheClearer())->clearPool('unknown'); + } + + /** + * @group legacy + * @expectedDeprecation The Symfony\Component\HttpKernel\CacheClearer\Psr6CacheClearer::addPool() method is deprecated since Symfony 3.3 and will be removed in 4.0. Pass an array of pools indexed by name to the constructor instead. + */ + public function testClearPoolsInjectedByAdder() + { + $pool1 = $this->getMockBuilder(CacheItemPoolInterface::class)->getMock(); + $pool1 + ->expects($this->once()) + ->method('clear'); + + $pool2 = $this->getMockBuilder(CacheItemPoolInterface::class)->getMock(); + $pool2 + ->expects($this->once()) + ->method('clear'); + + $clearer = new Psr6CacheClearer(array('pool1' => $pool1)); + $clearer->addPool($pool2); + $clearer->clear(''); + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/Tests/CacheWarmer/CacheWarmerAggregateTest.php b/lib/silex/vendor/symfony/http-kernel/Tests/CacheWarmer/CacheWarmerAggregateTest.php new file mode 100644 index 000000000..ba159124c --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Tests/CacheWarmer/CacheWarmerAggregateTest.php @@ -0,0 +1,107 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Tests\CacheWarmer; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\HttpKernel\CacheWarmer\CacheWarmerAggregate; + +class CacheWarmerAggregateTest extends TestCase +{ + protected static $cacheDir; + + public static function setUpBeforeClass() + { + self::$cacheDir = tempnam(sys_get_temp_dir(), 'sf2_cache_warmer_dir'); + } + + public static function tearDownAfterClass() + { + @unlink(self::$cacheDir); + } + + public function testInjectWarmersUsingConstructor() + { + $warmer = $this->getCacheWarmerMock(); + $warmer + ->expects($this->once()) + ->method('warmUp'); + $aggregate = new CacheWarmerAggregate(array($warmer)); + $aggregate->warmUp(self::$cacheDir); + } + + /** + * @group legacy + */ + public function testInjectWarmersUsingAdd() + { + $warmer = $this->getCacheWarmerMock(); + $warmer + ->expects($this->once()) + ->method('warmUp'); + $aggregate = new CacheWarmerAggregate(); + $aggregate->add($warmer); + $aggregate->warmUp(self::$cacheDir); + } + + /** + * @group legacy + */ + public function testInjectWarmersUsingSetWarmers() + { + $warmer = $this->getCacheWarmerMock(); + $warmer + ->expects($this->once()) + ->method('warmUp'); + $aggregate = new CacheWarmerAggregate(); + $aggregate->setWarmers(array($warmer)); + $aggregate->warmUp(self::$cacheDir); + } + + public function testWarmupDoesCallWarmupOnOptionalWarmersWhenEnableOptionalWarmersIsEnabled() + { + $warmer = $this->getCacheWarmerMock(); + $warmer + ->expects($this->never()) + ->method('isOptional'); + $warmer + ->expects($this->once()) + ->method('warmUp'); + + $aggregate = new CacheWarmerAggregate(array($warmer)); + $aggregate->enableOptionalWarmers(); + $aggregate->warmUp(self::$cacheDir); + } + + public function testWarmupDoesNotCallWarmupOnOptionalWarmersWhenEnableOptionalWarmersIsNotEnabled() + { + $warmer = $this->getCacheWarmerMock(); + $warmer + ->expects($this->once()) + ->method('isOptional') + ->will($this->returnValue(true)); + $warmer + ->expects($this->never()) + ->method('warmUp'); + + $aggregate = new CacheWarmerAggregate(array($warmer)); + $aggregate->warmUp(self::$cacheDir); + } + + protected function getCacheWarmerMock() + { + $warmer = $this->getMockBuilder('Symfony\Component\HttpKernel\CacheWarmer\CacheWarmerInterface') + ->disableOriginalConstructor() + ->getMock(); + + return $warmer; + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/Tests/CacheWarmer/CacheWarmerTest.php b/lib/silex/vendor/symfony/http-kernel/Tests/CacheWarmer/CacheWarmerTest.php new file mode 100644 index 000000000..05666cb0d --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Tests/CacheWarmer/CacheWarmerTest.php @@ -0,0 +1,68 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Tests\CacheWarmer; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\HttpKernel\CacheWarmer\CacheWarmer; + +class CacheWarmerTest extends TestCase +{ + protected static $cacheFile; + + public static function setUpBeforeClass() + { + self::$cacheFile = tempnam(sys_get_temp_dir(), 'sf2_cache_warmer_dir'); + } + + public static function tearDownAfterClass() + { + @unlink(self::$cacheFile); + } + + public function testWriteCacheFileCreatesTheFile() + { + $warmer = new TestCacheWarmer(self::$cacheFile); + $warmer->warmUp(dirname(self::$cacheFile)); + + $this->assertFileExists(self::$cacheFile); + } + + /** + * @expectedException \RuntimeException + */ + public function testWriteNonWritableCacheFileThrowsARuntimeException() + { + $nonWritableFile = '/this/file/is/very/probably/not/writable'; + $warmer = new TestCacheWarmer($nonWritableFile); + $warmer->warmUp(dirname($nonWritableFile)); + } +} + +class TestCacheWarmer extends CacheWarmer +{ + protected $file; + + public function __construct($file) + { + $this->file = $file; + } + + public function warmUp($cacheDir) + { + $this->writeCacheFile($this->file, 'content'); + } + + public function isOptional() + { + return false; + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/Tests/ClientTest.php b/lib/silex/vendor/symfony/http-kernel/Tests/ClientTest.php new file mode 100644 index 000000000..1ac72c735 --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Tests/ClientTest.php @@ -0,0 +1,183 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Tests; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\HttpKernel\Client; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpFoundation\StreamedResponse; +use Symfony\Component\HttpFoundation\Cookie; +use Symfony\Component\HttpFoundation\File\UploadedFile; +use Symfony\Component\HttpKernel\Tests\Fixtures\TestClient; + +/** + * @group time-sensitive + */ +class ClientTest extends TestCase +{ + public function testDoRequest() + { + $client = new Client(new TestHttpKernel()); + + $client->request('GET', '/'); + $this->assertEquals('Request: /', $client->getResponse()->getContent(), '->doRequest() uses the request handler to make the request'); + $this->assertInstanceOf('Symfony\Component\BrowserKit\Request', $client->getInternalRequest()); + $this->assertInstanceOf('Symfony\Component\HttpFoundation\Request', $client->getRequest()); + $this->assertInstanceOf('Symfony\Component\BrowserKit\Response', $client->getInternalResponse()); + $this->assertInstanceOf('Symfony\Component\HttpFoundation\Response', $client->getResponse()); + + $client->request('GET', 'http://www.example.com/'); + $this->assertEquals('Request: /', $client->getResponse()->getContent(), '->doRequest() uses the request handler to make the request'); + $this->assertEquals('www.example.com', $client->getRequest()->getHost(), '->doRequest() uses the request handler to make the request'); + + $client->request('GET', 'http://www.example.com/?parameter=http://google.com'); + $this->assertEquals('http://www.example.com/?parameter='.urlencode('http://google.com'), $client->getRequest()->getUri(), '->doRequest() uses the request handler to make the request'); + } + + public function testGetScript() + { + $client = new TestClient(new TestHttpKernel()); + $client->insulate(); + $client->request('GET', '/'); + + $this->assertEquals('Request: /', $client->getResponse()->getContent(), '->getScript() returns a script that uses the request handler to make the request'); + } + + public function testFilterResponseConvertsCookies() + { + $client = new Client(new TestHttpKernel()); + + $r = new \ReflectionObject($client); + $m = $r->getMethod('filterResponse'); + $m->setAccessible(true); + + $expected = array( + 'foo=bar; expires=Sun, 15-Feb-2009 20:00:00 GMT; max-age='.(strtotime('Sun, 15-Feb-2009 20:00:00 GMT') - time()).'; path=/foo; domain=http://example.com; secure; httponly', + 'foo1=bar1; expires=Sun, 15-Feb-2009 20:00:00 GMT; max-age='.(strtotime('Sun, 15-Feb-2009 20:00:00 GMT') - time()).'; path=/foo; domain=http://example.com; secure; httponly', + ); + + $response = new Response(); + $response->headers->setCookie(new Cookie('foo', 'bar', \DateTime::createFromFormat('j-M-Y H:i:s T', '15-Feb-2009 20:00:00 GMT')->format('U'), '/foo', 'http://example.com', true, true)); + $domResponse = $m->invoke($client, $response); + $this->assertEquals($expected[0], $domResponse->getHeader('Set-Cookie')); + + $response = new Response(); + $response->headers->setCookie(new Cookie('foo', 'bar', \DateTime::createFromFormat('j-M-Y H:i:s T', '15-Feb-2009 20:00:00 GMT')->format('U'), '/foo', 'http://example.com', true, true)); + $response->headers->setCookie(new Cookie('foo1', 'bar1', \DateTime::createFromFormat('j-M-Y H:i:s T', '15-Feb-2009 20:00:00 GMT')->format('U'), '/foo', 'http://example.com', true, true)); + $domResponse = $m->invoke($client, $response); + $this->assertEquals($expected[0], $domResponse->getHeader('Set-Cookie')); + $this->assertEquals($expected, $domResponse->getHeader('Set-Cookie', false)); + } + + public function testFilterResponseSupportsStreamedResponses() + { + $client = new Client(new TestHttpKernel()); + + $r = new \ReflectionObject($client); + $m = $r->getMethod('filterResponse'); + $m->setAccessible(true); + + $response = new StreamedResponse(function () { + echo 'foo'; + }); + + $domResponse = $m->invoke($client, $response); + $this->assertEquals('foo', $domResponse->getContent()); + } + + public function testUploadedFile() + { + $source = tempnam(sys_get_temp_dir(), 'source'); + $target = sys_get_temp_dir().'/sf.moved.file'; + @unlink($target); + + $kernel = new TestHttpKernel(); + $client = new Client($kernel); + + $files = array( + array('tmp_name' => $source, 'name' => 'original', 'type' => 'mime/original', 'size' => 123, 'error' => UPLOAD_ERR_OK), + new UploadedFile($source, 'original', 'mime/original', 123, UPLOAD_ERR_OK, true), + ); + + $file = null; + foreach ($files as $file) { + $client->request('POST', '/', array(), array('foo' => $file)); + + $files = $client->getRequest()->files->all(); + + $this->assertCount(1, $files); + + $file = $files['foo']; + + $this->assertEquals('original', $file->getClientOriginalName()); + $this->assertEquals('mime/original', $file->getClientMimeType()); + $this->assertEquals('123', $file->getClientSize()); + $this->assertTrue($file->isValid()); + } + + $file->move(dirname($target), basename($target)); + + $this->assertFileExists($target); + unlink($target); + } + + public function testUploadedFileWhenNoFileSelected() + { + $kernel = new TestHttpKernel(); + $client = new Client($kernel); + + $file = array('tmp_name' => '', 'name' => '', 'type' => '', 'size' => 0, 'error' => UPLOAD_ERR_NO_FILE); + + $client->request('POST', '/', array(), array('foo' => $file)); + + $files = $client->getRequest()->files->all(); + + $this->assertCount(1, $files); + $this->assertNull($files['foo']); + } + + public function testUploadedFileWhenSizeExceedsUploadMaxFileSize() + { + $source = tempnam(sys_get_temp_dir(), 'source'); + + $kernel = new TestHttpKernel(); + $client = new Client($kernel); + + $file = $this + ->getMockBuilder('Symfony\Component\HttpFoundation\File\UploadedFile') + ->setConstructorArgs(array($source, 'original', 'mime/original', 123, UPLOAD_ERR_OK, true)) + ->setMethods(array('getSize')) + ->getMock() + ; + + $file->expects($this->once()) + ->method('getSize') + ->will($this->returnValue(INF)) + ; + + $client->request('POST', '/', array(), array($file)); + + $files = $client->getRequest()->files->all(); + + $this->assertCount(1, $files); + + $file = $files[0]; + + $this->assertFalse($file->isValid()); + $this->assertEquals(UPLOAD_ERR_INI_SIZE, $file->getError()); + $this->assertEquals('mime/original', $file->getClientMimeType()); + $this->assertEquals('original', $file->getClientOriginalName()); + $this->assertEquals(0, $file->getClientSize()); + + unlink($source); + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/Tests/Config/EnvParametersResourceTest.php b/lib/silex/vendor/symfony/http-kernel/Tests/Config/EnvParametersResourceTest.php new file mode 100644 index 000000000..986e05d8b --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Tests/Config/EnvParametersResourceTest.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\HttpKernel\Tests\Config; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\HttpKernel\Config\EnvParametersResource; + +/** + * @group legacy + */ +class EnvParametersResourceTest extends TestCase +{ + protected $prefix = '__DUMMY_'; + protected $initialEnv; + protected $resource; + + protected function setUp() + { + $this->initialEnv = array( + $this->prefix.'1' => 'foo', + $this->prefix.'2' => 'bar', + ); + + foreach ($this->initialEnv as $key => $value) { + $_SERVER[$key] = $value; + } + + $this->resource = new EnvParametersResource($this->prefix); + } + + protected function tearDown() + { + foreach ($_SERVER as $key => $value) { + if (0 === strpos($key, $this->prefix)) { + unset($_SERVER[$key]); + } + } + } + + public function testGetResource() + { + $this->assertSame( + array('prefix' => $this->prefix, 'variables' => $this->initialEnv), + $this->resource->getResource(), + '->getResource() returns the resource' + ); + } + + public function testToString() + { + $this->assertSame( + serialize(array('prefix' => $this->prefix, 'variables' => $this->initialEnv)), + (string) $this->resource + ); + } + + public function testIsFreshNotChanged() + { + $this->assertTrue( + $this->resource->isFresh(time()), + '->isFresh() returns true if the variables have not changed' + ); + } + + public function testIsFreshValueChanged() + { + reset($this->initialEnv); + $_SERVER[key($this->initialEnv)] = 'baz'; + + $this->assertFalse( + $this->resource->isFresh(time()), + '->isFresh() returns false if a variable has been changed' + ); + } + + public function testIsFreshValueRemoved() + { + reset($this->initialEnv); + unset($_SERVER[key($this->initialEnv)]); + + $this->assertFalse( + $this->resource->isFresh(time()), + '->isFresh() returns false if a variable has been removed' + ); + } + + public function testIsFreshValueAdded() + { + $_SERVER[$this->prefix.'3'] = 'foo'; + + $this->assertFalse( + $this->resource->isFresh(time()), + '->isFresh() returns false if a variable has been added' + ); + } + + public function testSerializeUnserialize() + { + $this->assertEquals($this->resource, unserialize(serialize($this->resource))); + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/Tests/Config/FileLocatorTest.php b/lib/silex/vendor/symfony/http-kernel/Tests/Config/FileLocatorTest.php new file mode 100644 index 000000000..6265f0275 --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Tests/Config/FileLocatorTest.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\HttpKernel\Tests\Config; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\HttpKernel\Config\FileLocator; + +class FileLocatorTest extends TestCase +{ + public function testLocate() + { + $kernel = $this->getMockBuilder('Symfony\Component\HttpKernel\KernelInterface')->getMock(); + $kernel + ->expects($this->atLeastOnce()) + ->method('locateResource') + ->with('@BundleName/some/path', null, true) + ->will($this->returnValue('/bundle-name/some/path')); + $locator = new FileLocator($kernel); + $this->assertEquals('/bundle-name/some/path', $locator->locate('@BundleName/some/path')); + + $kernel + ->expects($this->never()) + ->method('locateResource'); + $this->{method_exists($this, $_ = 'expectException') ? $_ : 'setExpectedException'}('LogicException'); + $locator->locate('/some/path'); + } + + public function testLocateWithGlobalResourcePath() + { + $kernel = $this->getMockBuilder('Symfony\Component\HttpKernel\KernelInterface')->getMock(); + $kernel + ->expects($this->atLeastOnce()) + ->method('locateResource') + ->with('@BundleName/some/path', '/global/resource/path', false); + + $locator = new FileLocator($kernel, '/global/resource/path'); + $locator->locate('@BundleName/some/path', null, false); + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/Tests/Controller/ArgumentResolver/ServiceValueResolverTest.php b/lib/silex/vendor/symfony/http-kernel/Tests/Controller/ArgumentResolver/ServiceValueResolverTest.php new file mode 100644 index 000000000..b05828f5b --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Tests/Controller/ArgumentResolver/ServiceValueResolverTest.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\HttpKernel\Tests\Controller\ArgumentResolver; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\DependencyInjection\ServiceLocator; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpKernel\Controller\ArgumentResolver\ServiceValueResolver; +use Symfony\Component\HttpKernel\ControllerMetadata\ArgumentMetadata; + +class ServiceValueResolverTest extends TestCase +{ + public function testDoNotSupportWhenControllerDoNotExists() + { + $resolver = new ServiceValueResolver(new ServiceLocator(array())); + $argument = new ArgumentMetadata('dummy', DummyService::class, false, false, null); + $request = $this->requestWithAttributes(array('_controller' => 'my_controller')); + + $this->assertFalse($resolver->supports($request, $argument)); + } + + public function testExistingController() + { + $resolver = new ServiceValueResolver(new ServiceLocator(array( + 'App\\Controller\\Mine::method' => function () { + return new ServiceLocator(array( + 'dummy' => function () { + return new DummyService(); + }, + )); + }, + ))); + + $request = $this->requestWithAttributes(array('_controller' => 'App\\Controller\\Mine::method')); + $argument = new ArgumentMetadata('dummy', DummyService::class, false, false, null); + + $this->assertTrue($resolver->supports($request, $argument)); + $this->assertYieldEquals(array(new DummyService()), $resolver->resolve($request, $argument)); + } + + public function testControllerNameIsAnArray() + { + $resolver = new ServiceValueResolver(new ServiceLocator(array( + 'App\\Controller\\Mine::method' => function () { + return new ServiceLocator(array( + 'dummy' => function () { + return new DummyService(); + }, + )); + }, + ))); + + $request = $this->requestWithAttributes(array('_controller' => array('App\\Controller\\Mine', 'method'))); + $argument = new ArgumentMetadata('dummy', DummyService::class, false, false, null); + + $this->assertTrue($resolver->supports($request, $argument)); + $this->assertYieldEquals(array(new DummyService()), $resolver->resolve($request, $argument)); + } + + private function requestWithAttributes(array $attributes) + { + $request = Request::create('/'); + + foreach ($attributes as $name => $value) { + $request->attributes->set($name, $value); + } + + return $request; + } + + private function assertYieldEquals(array $expected, \Generator $generator) + { + $args = array(); + foreach ($generator as $arg) { + $args[] = $arg; + } + + $this->assertEquals($expected, $args); + } +} + +class DummyService +{ +} diff --git a/lib/silex/vendor/symfony/http-kernel/Tests/Controller/ArgumentResolverTest.php b/lib/silex/vendor/symfony/http-kernel/Tests/Controller/ArgumentResolverTest.php new file mode 100644 index 000000000..080413903 --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Tests/Controller/ArgumentResolverTest.php @@ -0,0 +1,349 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Tests\Controller; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\HttpFoundation\Session\Session; +use Symfony\Component\HttpFoundation\Session\SessionInterface; +use Symfony\Component\HttpFoundation\Session\Storage\MockArraySessionStorage; +use Symfony\Component\HttpKernel\Controller\ArgumentResolver; +use Symfony\Component\HttpKernel\Controller\ArgumentResolver\RequestAttributeValueResolver; +use Symfony\Component\HttpKernel\Controller\ArgumentValueResolverInterface; +use Symfony\Component\HttpKernel\ControllerMetadata\ArgumentMetadataFactory; +use Symfony\Component\HttpKernel\Tests\Fixtures\Controller\ExtendingRequest; +use Symfony\Component\HttpKernel\Tests\Fixtures\Controller\ExtendingSession; +use Symfony\Component\HttpKernel\Tests\Fixtures\Controller\NullableController; +use Symfony\Component\HttpKernel\Tests\Fixtures\Controller\VariadicController; +use Symfony\Component\HttpFoundation\Request; + +class ArgumentResolverTest extends TestCase +{ + /** @var ArgumentResolver */ + private static $resolver; + + public static function setUpBeforeClass() + { + $factory = new ArgumentMetadataFactory(); + + self::$resolver = new ArgumentResolver($factory); + } + + public function testDefaultState() + { + $this->assertEquals(self::$resolver, new ArgumentResolver()); + $this->assertNotEquals(self::$resolver, new ArgumentResolver(null, array(new RequestAttributeValueResolver()))); + } + + public function testGetArguments() + { + $request = Request::create('/'); + $request->attributes->set('foo', 'foo'); + $controller = array(new self(), 'controllerWithFoo'); + + $this->assertEquals(array('foo'), self::$resolver->getArguments($request, $controller), '->getArguments() returns an array of arguments for the controller method'); + } + + public function testGetArgumentsReturnsEmptyArrayWhenNoArguments() + { + $request = Request::create('/'); + $controller = array(new self(), 'controllerWithoutArguments'); + + $this->assertEquals(array(), self::$resolver->getArguments($request, $controller), '->getArguments() returns an empty array if the method takes no arguments'); + } + + public function testGetArgumentsUsesDefaultValue() + { + $request = Request::create('/'); + $request->attributes->set('foo', 'foo'); + $controller = array(new self(), 'controllerWithFooAndDefaultBar'); + + $this->assertEquals(array('foo', null), self::$resolver->getArguments($request, $controller), '->getArguments() uses default values if present'); + } + + public function testGetArgumentsOverrideDefaultValueByRequestAttribute() + { + $request = Request::create('/'); + $request->attributes->set('foo', 'foo'); + $request->attributes->set('bar', 'bar'); + $controller = array(new self(), 'controllerWithFooAndDefaultBar'); + + $this->assertEquals(array('foo', 'bar'), self::$resolver->getArguments($request, $controller), '->getArguments() overrides default values if provided in the request attributes'); + } + + public function testGetArgumentsFromClosure() + { + $request = Request::create('/'); + $request->attributes->set('foo', 'foo'); + $controller = function ($foo) {}; + + $this->assertEquals(array('foo'), self::$resolver->getArguments($request, $controller)); + } + + public function testGetArgumentsUsesDefaultValueFromClosure() + { + $request = Request::create('/'); + $request->attributes->set('foo', 'foo'); + $controller = function ($foo, $bar = 'bar') {}; + + $this->assertEquals(array('foo', 'bar'), self::$resolver->getArguments($request, $controller)); + } + + public function testGetArgumentsFromInvokableObject() + { + $request = Request::create('/'); + $request->attributes->set('foo', 'foo'); + $controller = new self(); + + $this->assertEquals(array('foo', null), self::$resolver->getArguments($request, $controller)); + + // Test default bar overridden by request attribute + $request->attributes->set('bar', 'bar'); + + $this->assertEquals(array('foo', 'bar'), self::$resolver->getArguments($request, $controller)); + } + + public function testGetArgumentsFromFunctionName() + { + $request = Request::create('/'); + $request->attributes->set('foo', 'foo'); + $request->attributes->set('foobar', 'foobar'); + $controller = __NAMESPACE__.'\controller_function'; + + $this->assertEquals(array('foo', 'foobar'), self::$resolver->getArguments($request, $controller)); + } + + public function testGetArgumentsFailsOnUnresolvedValue() + { + $request = Request::create('/'); + $request->attributes->set('foo', 'foo'); + $request->attributes->set('foobar', 'foobar'); + $controller = array(new self(), 'controllerWithFooBarFoobar'); + + try { + self::$resolver->getArguments($request, $controller); + $this->fail('->getArguments() throws a \RuntimeException exception if it cannot determine the argument value'); + } catch (\Exception $e) { + $this->assertInstanceOf('\RuntimeException', $e, '->getArguments() throws a \RuntimeException exception if it cannot determine the argument value'); + } + } + + public function testGetArgumentsInjectsRequest() + { + $request = Request::create('/'); + $controller = array(new self(), 'controllerWithRequest'); + + $this->assertEquals(array($request), self::$resolver->getArguments($request, $controller), '->getArguments() injects the request'); + } + + public function testGetArgumentsInjectsExtendingRequest() + { + $request = ExtendingRequest::create('/'); + $controller = array(new self(), 'controllerWithExtendingRequest'); + + $this->assertEquals(array($request), self::$resolver->getArguments($request, $controller), '->getArguments() injects the request when extended'); + } + + /** + * @requires PHP 5.6 + */ + public function testGetVariadicArguments() + { + $request = Request::create('/'); + $request->attributes->set('foo', 'foo'); + $request->attributes->set('bar', array('foo', 'bar')); + $controller = array(new VariadicController(), 'action'); + + $this->assertEquals(array('foo', 'foo', 'bar'), self::$resolver->getArguments($request, $controller)); + } + + /** + * @requires PHP 5.6 + * @expectedException \InvalidArgumentException + */ + public function testGetVariadicArgumentsWithoutArrayInRequest() + { + $request = Request::create('/'); + $request->attributes->set('foo', 'foo'); + $request->attributes->set('bar', 'foo'); + $controller = array(new VariadicController(), 'action'); + + self::$resolver->getArguments($request, $controller); + } + + /** + * @requires PHP 5.6 + * @expectedException \InvalidArgumentException + */ + public function testGetArgumentWithoutArray() + { + $factory = new ArgumentMetadataFactory(); + $valueResolver = $this->getMockBuilder(ArgumentValueResolverInterface::class)->getMock(); + $resolver = new ArgumentResolver($factory, array($valueResolver)); + + $valueResolver->expects($this->any())->method('supports')->willReturn(true); + $valueResolver->expects($this->any())->method('resolve')->willReturn('foo'); + + $request = Request::create('/'); + $request->attributes->set('foo', 'foo'); + $request->attributes->set('bar', 'foo'); + $controller = array($this, 'controllerWithFooAndDefaultBar'); + $resolver->getArguments($request, $controller); + } + + /** + * @expectedException \RuntimeException + */ + public function testIfExceptionIsThrownWhenMissingAnArgument() + { + $request = Request::create('/'); + $controller = array($this, 'controllerWithFoo'); + + self::$resolver->getArguments($request, $controller); + } + + /** + * @requires PHP 7.1 + */ + public function testGetNullableArguments() + { + $request = Request::create('/'); + $request->attributes->set('foo', 'foo'); + $request->attributes->set('bar', new \stdClass()); + $request->attributes->set('mandatory', 'mandatory'); + $controller = array(new NullableController(), 'action'); + + $this->assertEquals(array('foo', new \stdClass(), 'value', 'mandatory'), self::$resolver->getArguments($request, $controller)); + } + + /** + * @requires PHP 7.1 + */ + public function testGetNullableArgumentsWithDefaults() + { + $request = Request::create('/'); + $request->attributes->set('mandatory', 'mandatory'); + $controller = array(new NullableController(), 'action'); + + $this->assertEquals(array(null, null, 'value', 'mandatory'), self::$resolver->getArguments($request, $controller)); + } + + public function testGetSessionArguments() + { + $session = new Session(new MockArraySessionStorage()); + $request = Request::create('/'); + $request->setSession($session); + $controller = array($this, 'controllerWithSession'); + + $this->assertEquals(array($session), self::$resolver->getArguments($request, $controller)); + } + + public function testGetSessionArgumentsWithExtendedSession() + { + $session = new ExtendingSession(new MockArraySessionStorage()); + $request = Request::create('/'); + $request->setSession($session); + $controller = array($this, 'controllerWithExtendingSession'); + + $this->assertEquals(array($session), self::$resolver->getArguments($request, $controller)); + } + + public function testGetSessionArgumentsWithInterface() + { + $session = $this->getMockBuilder(SessionInterface::class)->getMock(); + $request = Request::create('/'); + $request->setSession($session); + $controller = array($this, 'controllerWithSessionInterface'); + + $this->assertEquals(array($session), self::$resolver->getArguments($request, $controller)); + } + + /** + * @expectedException \RuntimeException + */ + public function testGetSessionMissMatchWithInterface() + { + $session = $this->getMockBuilder(SessionInterface::class)->getMock(); + $request = Request::create('/'); + $request->setSession($session); + $controller = array($this, 'controllerWithExtendingSession'); + + self::$resolver->getArguments($request, $controller); + } + + /** + * @expectedException \RuntimeException + */ + public function testGetSessionMissMatchWithImplementation() + { + $session = new Session(new MockArraySessionStorage()); + $request = Request::create('/'); + $request->setSession($session); + $controller = array($this, 'controllerWithExtendingSession'); + + self::$resolver->getArguments($request, $controller); + } + + /** + * @expectedException \RuntimeException + */ + public function testGetSessionMissMatchOnNull() + { + $request = Request::create('/'); + $controller = array($this, 'controllerWithExtendingSession'); + + self::$resolver->getArguments($request, $controller); + } + + public function __invoke($foo, $bar = null) + { + } + + public function controllerWithFoo($foo) + { + } + + public function controllerWithoutArguments() + { + } + + protected function controllerWithFooAndDefaultBar($foo, $bar = null) + { + } + + protected function controllerWithFooBarFoobar($foo, $bar, $foobar) + { + } + + protected function controllerWithRequest(Request $request) + { + } + + protected function controllerWithExtendingRequest(ExtendingRequest $request) + { + } + + protected function controllerWithSession(Session $session) + { + } + + protected function controllerWithSessionInterface(SessionInterface $session) + { + } + + protected function controllerWithExtendingSession(ExtendingSession $session) + { + } +} + +function controller_function($foo, $foobar) +{ +} diff --git a/lib/silex/vendor/symfony/http-kernel/Tests/Controller/ContainerControllerResolverTest.php b/lib/silex/vendor/symfony/http-kernel/Tests/Controller/ContainerControllerResolverTest.php new file mode 100644 index 000000000..0019123b6 --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Tests/Controller/ContainerControllerResolverTest.php @@ -0,0 +1,308 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Tests\Controller; + +use Psr\Container\ContainerInterface; +use Psr\Log\LoggerInterface; +use Symfony\Component\Debug\ErrorHandler; +use Symfony\Component\DependencyInjection\Container; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpKernel\Controller\ContainerControllerResolver; + +class ContainerControllerResolverTest extends ControllerResolverTest +{ + public function testGetControllerService() + { + $container = $this->createMockContainer(); + $container->expects($this->once()) + ->method('has') + ->with('foo') + ->will($this->returnValue(true)); + $container->expects($this->once()) + ->method('get') + ->with('foo') + ->will($this->returnValue($this)) + ; + + $resolver = $this->createControllerResolver(null, $container); + $request = Request::create('/'); + $request->attributes->set('_controller', 'foo:controllerMethod1'); + + $controller = $resolver->getController($request); + + $this->assertInstanceOf(get_class($this), $controller[0]); + $this->assertSame('controllerMethod1', $controller[1]); + } + + public function testGetControllerInvokableService() + { + $invokableController = new InvokableController('bar'); + + $container = $this->createMockContainer(); + $container->expects($this->once()) + ->method('has') + ->with('foo') + ->will($this->returnValue(true)) + ; + $container->expects($this->once()) + ->method('get') + ->with('foo') + ->will($this->returnValue($invokableController)) + ; + + $resolver = $this->createControllerResolver(null, $container); + $request = Request::create('/'); + $request->attributes->set('_controller', 'foo'); + + $controller = $resolver->getController($request); + + $this->assertEquals($invokableController, $controller); + } + + public function testGetControllerInvokableServiceWithClassNameAsName() + { + $invokableController = new InvokableController('bar'); + $className = __NAMESPACE__.'\InvokableController'; + + $container = $this->createMockContainer(); + $container->expects($this->once()) + ->method('has') + ->with($className) + ->will($this->returnValue(true)) + ; + $container->expects($this->once()) + ->method('get') + ->with($className) + ->will($this->returnValue($invokableController)) + ; + + $resolver = $this->createControllerResolver(null, $container); + $request = Request::create('/'); + $request->attributes->set('_controller', $className); + + $controller = $resolver->getController($request); + + $this->assertEquals($invokableController, $controller); + } + + public function testNonInstantiableController() + { + $container = $this->createMockContainer(); + $container->expects($this->once()) + ->method('has') + ->with(NonInstantiableController::class) + ->will($this->returnValue(false)) + ; + + $resolver = $this->createControllerResolver(null, $container); + $request = Request::create('/'); + $request->attributes->set('_controller', array(NonInstantiableController::class, 'action')); + + $controller = $resolver->getController($request); + + $this->assertSame(array(NonInstantiableController::class, 'action'), $controller); + } + + /** + * @expectedException \LogicException + * @expectedExceptionMessage Controller "Symfony\Component\HttpKernel\Tests\Controller\ImpossibleConstructController" cannot be fetched from the container because it is private. Did you forget to tag the service with "controller.service_arguments"? + */ + public function testNonConstructController() + { + $container = $this->getMockBuilder(Container::class)->getMock(); + $container->expects($this->at(0)) + ->method('has') + ->with(ImpossibleConstructController::class) + ->will($this->returnValue(true)) + ; + + $container->expects($this->at(1)) + ->method('has') + ->with(ImpossibleConstructController::class) + ->will($this->returnValue(false)) + ; + + $container->expects($this->atLeastOnce()) + ->method('getRemovedIds') + ->with() + ->will($this->returnValue(array(ImpossibleConstructController::class => true))) + ; + + $resolver = $this->createControllerResolver(null, $container); + $request = Request::create('/'); + $request->attributes->set('_controller', array(ImpossibleConstructController::class, 'action')); + + if (\PHP_VERSION_ID < 70100) { + ErrorHandler::register(); + try { + $resolver->getController($request); + } finally { + restore_error_handler(); + restore_exception_handler(); + } + } else { + $resolver->getController($request); + } + } + + public function testNonInstantiableControllerWithCorrespondingService() + { + $service = new \stdClass(); + + $container = $this->createMockContainer(); + $container->expects($this->atLeastOnce()) + ->method('has') + ->with(NonInstantiableController::class) + ->will($this->returnValue(true)) + ; + $container->expects($this->atLeastOnce()) + ->method('get') + ->with(NonInstantiableController::class) + ->will($this->returnValue($service)) + ; + + $resolver = $this->createControllerResolver(null, $container); + $request = Request::create('/'); + $request->attributes->set('_controller', array(NonInstantiableController::class, 'action')); + + $controller = $resolver->getController($request); + + $this->assertSame(array($service, 'action'), $controller); + } + + /** + * @expectedException \LogicException + * @expectedExceptionMessage Controller "app.my_controller" cannot be fetched from the container because it is private. Did you forget to tag the service with "controller.service_arguments"? + */ + public function testExceptionWhenUsingRemovedControllerService() + { + $container = $this->getMockBuilder(Container::class)->getMock(); + $container->expects($this->at(0)) + ->method('has') + ->with('app.my_controller') + ->will($this->returnValue(false)) + ; + + $container->expects($this->atLeastOnce()) + ->method('getRemovedIds') + ->with() + ->will($this->returnValue(array('app.my_controller' => true))) + ; + + $resolver = $this->createControllerResolver(null, $container); + + $request = Request::create('/'); + $request->attributes->set('_controller', 'app.my_controller'); + $resolver->getController($request); + } + + /** + * @expectedException \LogicException + * @expectedExceptionMessage Controller "app.my_controller" cannot be called without a method name. Did you forget an "__invoke" method? + */ + public function testExceptionWhenUsingControllerWithoutAnInvokeMethod() + { + $container = $this->getMockBuilder(Container::class)->getMock(); + $container->expects($this->once()) + ->method('has') + ->with('app.my_controller') + ->will($this->returnValue(true)) + ; + $container->expects($this->once()) + ->method('get') + ->with('app.my_controller') + ->will($this->returnValue(new ImpossibleConstructController('toto', 'controller'))) + ; + + $resolver = $this->createControllerResolver(null, $container); + + $request = Request::create('/'); + $request->attributes->set('_controller', 'app.my_controller'); + $resolver->getController($request); + } + + /** + * @dataProvider getUndefinedControllers + */ + public function testGetControllerOnNonUndefinedFunction($controller, $exceptionName = null, $exceptionMessage = null) + { + // All this logic needs to be duplicated, since calling parent::testGetControllerOnNonUndefinedFunction will override the expected excetion and not use the regex + $resolver = $this->createControllerResolver(); + if (method_exists($this, 'expectException')) { + $this->expectException($exceptionName); + $this->expectExceptionMessageRegExp($exceptionMessage); + } else { + $this->setExpectedExceptionRegExp($exceptionName, $exceptionMessage); + } + + $request = Request::create('/'); + $request->attributes->set('_controller', $controller); + $resolver->getController($request); + } + + public function getUndefinedControllers() + { + return array( + array('foo', \LogicException::class, '/Controller not found: service "foo" does not exist\./'), + array('oof::bar', \InvalidArgumentException::class, '/Class "oof" does not exist\./'), + array('stdClass', \LogicException::class, '/Controller not found: service "stdClass" does not exist\./'), + array( + 'Symfony\Component\HttpKernel\Tests\Controller\ControllerResolverTest::bar', + \InvalidArgumentException::class, + '/.?[cC]ontroller(.*?) for URI "\/" is not callable\.( Expected method(.*) Available methods)?/', + ), + ); + } + + protected function createControllerResolver(LoggerInterface $logger = null, ContainerInterface $container = null) + { + if (!$container) { + $container = $this->createMockContainer(); + } + + return new ContainerControllerResolver($container, $logger); + } + + protected function createMockContainer() + { + return $this->getMockBuilder(ContainerInterface::class)->getMock(); + } +} + +class InvokableController +{ + public function __construct($bar) // mandatory argument to prevent automatic instantiation + { + } + + public function __invoke() + { + } +} + +abstract class NonInstantiableController +{ + public static function action() + { + } +} + +class ImpossibleConstructController +{ + public function __construct($toto, $controller) + { + } + + public function action() + { + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/Tests/Controller/ControllerResolverTest.php b/lib/silex/vendor/symfony/http-kernel/Tests/Controller/ControllerResolverTest.php new file mode 100644 index 000000000..190e15ad6 --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Tests/Controller/ControllerResolverTest.php @@ -0,0 +1,331 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Tests\Controller; + +use PHPUnit\Framework\TestCase; +use Psr\Log\LoggerInterface; +use Symfony\Component\HttpKernel\Controller\ControllerResolver; +use Symfony\Component\HttpKernel\Tests\Fixtures\Controller\NullableController; +use Symfony\Component\HttpKernel\Tests\Fixtures\Controller\VariadicController; +use Symfony\Component\HttpFoundation\Request; + +class ControllerResolverTest extends TestCase +{ + public function testGetControllerWithoutControllerParameter() + { + $logger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock(); + $logger->expects($this->once())->method('warning')->with('Unable to look for the controller as the "_controller" parameter is missing.'); + $resolver = $this->createControllerResolver($logger); + + $request = Request::create('/'); + $this->assertFalse($resolver->getController($request), '->getController() returns false when the request has no _controller attribute'); + } + + public function testGetControllerWithLambda() + { + $resolver = $this->createControllerResolver(); + + $request = Request::create('/'); + $request->attributes->set('_controller', $lambda = function () {}); + $controller = $resolver->getController($request); + $this->assertSame($lambda, $controller); + } + + public function testGetControllerWithObjectAndInvokeMethod() + { + $resolver = $this->createControllerResolver(); + + $request = Request::create('/'); + $request->attributes->set('_controller', $this); + $controller = $resolver->getController($request); + $this->assertSame($this, $controller); + } + + public function testGetControllerWithObjectAndMethod() + { + $resolver = $this->createControllerResolver(); + + $request = Request::create('/'); + $request->attributes->set('_controller', array($this, 'controllerMethod1')); + $controller = $resolver->getController($request); + $this->assertSame(array($this, 'controllerMethod1'), $controller); + } + + public function testGetControllerWithClassAndMethod() + { + $resolver = $this->createControllerResolver(); + + $request = Request::create('/'); + $request->attributes->set('_controller', array('Symfony\Component\HttpKernel\Tests\Controller\ControllerResolverTest', 'controllerMethod4')); + $controller = $resolver->getController($request); + $this->assertSame(array('Symfony\Component\HttpKernel\Tests\Controller\ControllerResolverTest', 'controllerMethod4'), $controller); + } + + public function testGetControllerWithObjectAndMethodAsString() + { + $resolver = $this->createControllerResolver(); + + $request = Request::create('/'); + $request->attributes->set('_controller', 'Symfony\Component\HttpKernel\Tests\Controller\ControllerResolverTest::controllerMethod1'); + $controller = $resolver->getController($request); + $this->assertInstanceOf('Symfony\Component\HttpKernel\Tests\Controller\ControllerResolverTest', $controller[0], '->getController() returns a PHP callable'); + } + + public function testGetControllerWithClassAndInvokeMethod() + { + $resolver = $this->createControllerResolver(); + + $request = Request::create('/'); + $request->attributes->set('_controller', 'Symfony\Component\HttpKernel\Tests\Controller\ControllerResolverTest'); + $controller = $resolver->getController($request); + $this->assertInstanceOf('Symfony\Component\HttpKernel\Tests\Controller\ControllerResolverTest', $controller); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testGetControllerOnObjectWithoutInvokeMethod() + { + $resolver = $this->createControllerResolver(); + + $request = Request::create('/'); + $request->attributes->set('_controller', new \stdClass()); + $resolver->getController($request); + } + + public function testGetControllerWithFunction() + { + $resolver = $this->createControllerResolver(); + + $request = Request::create('/'); + $request->attributes->set('_controller', 'Symfony\Component\HttpKernel\Tests\Controller\some_controller_function'); + $controller = $resolver->getController($request); + $this->assertSame('Symfony\Component\HttpKernel\Tests\Controller\some_controller_function', $controller); + } + + /** + * @dataProvider getUndefinedControllers + */ + public function testGetControllerOnNonUndefinedFunction($controller, $exceptionName = null, $exceptionMessage = null) + { + $resolver = $this->createControllerResolver(); + if (method_exists($this, 'expectException')) { + $this->expectException($exceptionName); + $this->expectExceptionMessage($exceptionMessage); + } else { + $this->setExpectedException($exceptionName, $exceptionMessage); + } + + $request = Request::create('/'); + $request->attributes->set('_controller', $controller); + $resolver->getController($request); + } + + public function getUndefinedControllers() + { + return array( + array(1, 'InvalidArgumentException', 'Unable to find controller "1".'), + array('foo', 'InvalidArgumentException', 'Unable to find controller "foo".'), + array('oof::bar', 'InvalidArgumentException', 'Class "oof" does not exist.'), + array('stdClass', 'InvalidArgumentException', 'Unable to find controller "stdClass".'), + array('Symfony\Component\HttpKernel\Tests\Controller\ControllerTest::staticsAction', 'InvalidArgumentException', 'The controller for URI "/" is not callable. Expected method "staticsAction" on class "Symfony\Component\HttpKernel\Tests\Controller\ControllerTest", did you mean "staticAction"?'), + array('Symfony\Component\HttpKernel\Tests\Controller\ControllerTest::privateAction', 'InvalidArgumentException', 'The controller for URI "/" is not callable. Method "privateAction" on class "Symfony\Component\HttpKernel\Tests\Controller\ControllerTest" should be public and non-abstract'), + array('Symfony\Component\HttpKernel\Tests\Controller\ControllerTest::protectedAction', 'InvalidArgumentException', 'The controller for URI "/" is not callable. Method "protectedAction" on class "Symfony\Component\HttpKernel\Tests\Controller\ControllerTest" should be public and non-abstract'), + array('Symfony\Component\HttpKernel\Tests\Controller\ControllerTest::undefinedAction', 'InvalidArgumentException', 'The controller for URI "/" is not callable. Expected method "undefinedAction" on class "Symfony\Component\HttpKernel\Tests\Controller\ControllerTest". Available methods: "publicAction", "staticAction"'), + ); + } + + /** + * @group legacy + */ + public function testGetArguments() + { + $resolver = $this->createControllerResolver(); + + $request = Request::create('/'); + $controller = array(new self(), 'testGetArguments'); + $this->assertEquals(array(), $resolver->getArguments($request, $controller), '->getArguments() returns an empty array if the method takes no arguments'); + + $request = Request::create('/'); + $request->attributes->set('foo', 'foo'); + $controller = array(new self(), 'controllerMethod1'); + $this->assertEquals(array('foo'), $resolver->getArguments($request, $controller), '->getArguments() returns an array of arguments for the controller method'); + + $request = Request::create('/'); + $request->attributes->set('foo', 'foo'); + $controller = array(new self(), 'controllerMethod2'); + $this->assertEquals(array('foo', null), $resolver->getArguments($request, $controller), '->getArguments() uses default values if present'); + + $request->attributes->set('bar', 'bar'); + $this->assertEquals(array('foo', 'bar'), $resolver->getArguments($request, $controller), '->getArguments() overrides default values if provided in the request attributes'); + + $request = Request::create('/'); + $request->attributes->set('foo', 'foo'); + $controller = function ($foo) {}; + $this->assertEquals(array('foo'), $resolver->getArguments($request, $controller)); + + $request = Request::create('/'); + $request->attributes->set('foo', 'foo'); + $controller = function ($foo, $bar = 'bar') {}; + $this->assertEquals(array('foo', 'bar'), $resolver->getArguments($request, $controller)); + + $request = Request::create('/'); + $request->attributes->set('foo', 'foo'); + $controller = new self(); + $this->assertEquals(array('foo', null), $resolver->getArguments($request, $controller)); + $request->attributes->set('bar', 'bar'); + $this->assertEquals(array('foo', 'bar'), $resolver->getArguments($request, $controller)); + + $request = Request::create('/'); + $request->attributes->set('foo', 'foo'); + $request->attributes->set('foobar', 'foobar'); + $controller = 'Symfony\Component\HttpKernel\Tests\Controller\some_controller_function'; + $this->assertEquals(array('foo', 'foobar'), $resolver->getArguments($request, $controller)); + + $request = Request::create('/'); + $request->attributes->set('foo', 'foo'); + $request->attributes->set('foobar', 'foobar'); + $controller = array(new self(), 'controllerMethod3'); + + try { + $resolver->getArguments($request, $controller); + $this->fail('->getArguments() throws a \RuntimeException exception if it cannot determine the argument value'); + } catch (\Exception $e) { + $this->assertInstanceOf('\RuntimeException', $e, '->getArguments() throws a \RuntimeException exception if it cannot determine the argument value'); + } + + $request = Request::create('/'); + $controller = array(new self(), 'controllerMethod5'); + $this->assertEquals(array($request), $resolver->getArguments($request, $controller), '->getArguments() injects the request'); + } + + /** + * @requires PHP 5.6 + * @group legacy + */ + public function testGetVariadicArguments() + { + $resolver = new ControllerResolver(); + + $request = Request::create('/'); + $request->attributes->set('foo', 'foo'); + $request->attributes->set('bar', array('foo', 'bar')); + $controller = array(new VariadicController(), 'action'); + $this->assertEquals(array('foo', 'foo', 'bar'), $resolver->getArguments($request, $controller)); + } + + public function testCreateControllerCanReturnAnyCallable() + { + $mock = $this->getMockBuilder('Symfony\Component\HttpKernel\Controller\ControllerResolver')->setMethods(array('createController'))->getMock(); + $mock->expects($this->once())->method('createController')->will($this->returnValue('Symfony\Component\HttpKernel\Tests\Controller\some_controller_function')); + + $request = Request::create('/'); + $request->attributes->set('_controller', 'foobar'); + $mock->getController($request); + } + + /** + * @expectedException \RuntimeException + * @group legacy + */ + public function testIfExceptionIsThrownWhenMissingAnArgument() + { + $resolver = new ControllerResolver(); + $request = Request::create('/'); + + $controller = array($this, 'controllerMethod1'); + + $resolver->getArguments($request, $controller); + } + + /** + * @requires PHP 7.1 + * @group legacy + */ + public function testGetNullableArguments() + { + $resolver = new ControllerResolver(); + + $request = Request::create('/'); + $request->attributes->set('foo', 'foo'); + $request->attributes->set('bar', new \stdClass()); + $request->attributes->set('mandatory', 'mandatory'); + $controller = array(new NullableController(), 'action'); + $this->assertEquals(array('foo', new \stdClass(), 'value', 'mandatory'), $resolver->getArguments($request, $controller)); + } + + /** + * @requires PHP 7.1 + * @group legacy + */ + public function testGetNullableArgumentsWithDefaults() + { + $resolver = new ControllerResolver(); + + $request = Request::create('/'); + $request->attributes->set('mandatory', 'mandatory'); + $controller = array(new NullableController(), 'action'); + $this->assertEquals(array(null, null, 'value', 'mandatory'), $resolver->getArguments($request, $controller)); + } + + protected function createControllerResolver(LoggerInterface $logger = null) + { + return new ControllerResolver($logger); + } + + public function __invoke($foo, $bar = null) + { + } + + public function controllerMethod1($foo) + { + } + + protected function controllerMethod2($foo, $bar = null) + { + } + + protected function controllerMethod3($foo, $bar, $foobar) + { + } + + protected static function controllerMethod4() + { + } + + protected function controllerMethod5(Request $request) + { + } +} + +function some_controller_function($foo, $foobar) +{ +} + +class ControllerTest +{ + public function publicAction() + { + } + + private function privateAction() + { + } + + protected function protectedAction() + { + } + + public static function staticAction() + { + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/Tests/ControllerMetadata/ArgumentMetadataFactoryTest.php b/lib/silex/vendor/symfony/http-kernel/Tests/ControllerMetadata/ArgumentMetadataFactoryTest.php new file mode 100644 index 000000000..b4b449f35 --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Tests/ControllerMetadata/ArgumentMetadataFactoryTest.php @@ -0,0 +1,148 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Tests\ControllerMetadata; + +use Fake\ImportedAndFake; +use PHPUnit\Framework\TestCase; +use Symfony\Component\HttpKernel\ControllerMetadata\ArgumentMetadata; +use Symfony\Component\HttpKernel\ControllerMetadata\ArgumentMetadataFactory; +use Symfony\Component\HttpKernel\Tests\Fixtures\Controller\BasicTypesController; +use Symfony\Component\HttpKernel\Tests\Fixtures\Controller\NullableController; +use Symfony\Component\HttpKernel\Tests\Fixtures\Controller\VariadicController; + +class ArgumentMetadataFactoryTest extends TestCase +{ + /** + * @var ArgumentMetadataFactory + */ + private $factory; + + protected function setUp() + { + $this->factory = new ArgumentMetadataFactory(); + } + + public function testSignature1() + { + $arguments = $this->factory->createArgumentMetadata(array($this, 'signature1')); + + $this->assertEquals(array( + new ArgumentMetadata('foo', self::class, false, false, null), + new ArgumentMetadata('bar', 'array', false, false, null), + new ArgumentMetadata('baz', 'callable', false, false, null), + ), $arguments); + } + + public function testSignature2() + { + $arguments = $this->factory->createArgumentMetadata(array($this, 'signature2')); + + $this->assertEquals(array( + new ArgumentMetadata('foo', self::class, false, true, null, true), + new ArgumentMetadata('bar', __NAMESPACE__.'\FakeClassThatDoesNotExist', false, true, null, true), + new ArgumentMetadata('baz', 'Fake\ImportedAndFake', false, true, null, true), + ), $arguments); + } + + public function testSignature3() + { + $arguments = $this->factory->createArgumentMetadata(array($this, 'signature3')); + + $this->assertEquals(array( + new ArgumentMetadata('bar', __NAMESPACE__.'\FakeClassThatDoesNotExist', false, false, null), + new ArgumentMetadata('baz', 'Fake\ImportedAndFake', false, false, null), + ), $arguments); + } + + public function testSignature4() + { + $arguments = $this->factory->createArgumentMetadata(array($this, 'signature4')); + + $this->assertEquals(array( + new ArgumentMetadata('foo', null, false, true, 'default'), + new ArgumentMetadata('bar', null, false, true, 500), + new ArgumentMetadata('baz', null, false, true, array()), + ), $arguments); + } + + public function testSignature5() + { + $arguments = $this->factory->createArgumentMetadata(array($this, 'signature5')); + + $this->assertEquals(array( + new ArgumentMetadata('foo', 'array', false, true, null, true), + new ArgumentMetadata('bar', null, false, false, null), + ), $arguments); + } + + /** + * @requires PHP 5.6 + */ + public function testVariadicSignature() + { + $arguments = $this->factory->createArgumentMetadata(array(new VariadicController(), 'action')); + + $this->assertEquals(array( + new ArgumentMetadata('foo', null, false, false, null), + new ArgumentMetadata('bar', null, true, false, null), + ), $arguments); + } + + /** + * @requires PHP 7.0 + */ + public function testBasicTypesSignature() + { + $arguments = $this->factory->createArgumentMetadata(array(new BasicTypesController(), 'action')); + + $this->assertEquals(array( + new ArgumentMetadata('foo', 'string', false, false, null), + new ArgumentMetadata('bar', 'int', false, false, null), + new ArgumentMetadata('baz', 'float', false, false, null), + ), $arguments); + } + + /** + * @requires PHP 7.1 + */ + public function testNullableTypesSignature() + { + $arguments = $this->factory->createArgumentMetadata(array(new NullableController(), 'action')); + + $this->assertEquals(array( + new ArgumentMetadata('foo', 'string', false, false, null, true), + new ArgumentMetadata('bar', \stdClass::class, false, false, null, true), + new ArgumentMetadata('baz', 'string', false, true, 'value', true), + new ArgumentMetadata('mandatory', null, false, false, null, true), + ), $arguments); + } + + private function signature1(ArgumentMetadataFactoryTest $foo, array $bar, callable $baz) + { + } + + private function signature2(ArgumentMetadataFactoryTest $foo = null, FakeClassThatDoesNotExist $bar = null, ImportedAndFake $baz = null) + { + } + + private function signature3(FakeClassThatDoesNotExist $bar, ImportedAndFake $baz) + { + } + + private function signature4($foo = 'default', $bar = 500, $baz = array()) + { + } + + private function signature5(array $foo = null, $bar) + { + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/Tests/ControllerMetadata/ArgumentMetadataTest.php b/lib/silex/vendor/symfony/http-kernel/Tests/ControllerMetadata/ArgumentMetadataTest.php new file mode 100644 index 000000000..05351445e --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Tests/ControllerMetadata/ArgumentMetadataTest.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\HttpKernel\Tests\ControllerMetadata; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\HttpKernel\ControllerMetadata\ArgumentMetadata; + +class ArgumentMetadataTest extends TestCase +{ + public function testWithBcLayerWithDefault() + { + $argument = new ArgumentMetadata('foo', 'string', false, true, 'default value'); + + $this->assertFalse($argument->isNullable()); + } + + public function testDefaultValueAvailable() + { + $argument = new ArgumentMetadata('foo', 'string', false, true, 'default value', true); + + $this->assertTrue($argument->isNullable()); + $this->assertTrue($argument->hasDefaultValue()); + $this->assertSame('default value', $argument->getDefaultValue()); + } + + /** + * @expectedException \LogicException + */ + public function testDefaultValueUnavailable() + { + $argument = new ArgumentMetadata('foo', 'string', false, false, null, false); + + $this->assertFalse($argument->isNullable()); + $this->assertFalse($argument->hasDefaultValue()); + $argument->getDefaultValue(); + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/Tests/DataCollector/Compiler.log b/lib/silex/vendor/symfony/http-kernel/Tests/DataCollector/Compiler.log new file mode 100644 index 000000000..88b6840ea --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Tests/DataCollector/Compiler.log @@ -0,0 +1,4 @@ +Symfony\Component\DependencyInjection\Compiler\RemovePrivateAliasesPass: Removed service "Psr\Container\ContainerInterface"; reason: private alias. +Symfony\Component\DependencyInjection\Compiler\RemovePrivateAliasesPass: Removed service "Symfony\Component\DependencyInjection\ContainerInterface"; reason: private alias. +Some custom logging message +With ending : diff --git a/lib/silex/vendor/symfony/http-kernel/Tests/DataCollector/ConfigDataCollectorTest.php b/lib/silex/vendor/symfony/http-kernel/Tests/DataCollector/ConfigDataCollectorTest.php new file mode 100644 index 000000000..4fb36afda --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Tests/DataCollector/ConfigDataCollectorTest.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\HttpKernel\Tests\DataCollector; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\HttpKernel\DataCollector\ConfigDataCollector; +use Symfony\Component\HttpKernel\Kernel; +use Symfony\Component\Config\Loader\LoaderInterface; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +class ConfigDataCollectorTest extends TestCase +{ + public function testCollect() + { + $kernel = new KernelForTest('test', true); + $c = new ConfigDataCollector(); + $c->setKernel($kernel); + $c->collect(new Request(), new Response()); + + $this->assertSame('test', $c->getEnv()); + $this->assertTrue($c->isDebug()); + $this->assertSame('config', $c->getName()); + $this->assertSame('testkernel', $c->getAppName()); + $this->assertRegExp('~^'.preg_quote($c->getPhpVersion(), '~').'~', PHP_VERSION); + $this->assertRegExp('~'.preg_quote((string) $c->getPhpVersionExtra(), '~').'$~', PHP_VERSION); + $this->assertSame(PHP_INT_SIZE * 8, $c->getPhpArchitecture()); + $this->assertSame(class_exists('Locale', false) && \Locale::getDefault() ? \Locale::getDefault() : 'n/a', $c->getPhpIntlLocale()); + $this->assertSame(date_default_timezone_get(), $c->getPhpTimezone()); + $this->assertSame(Kernel::VERSION, $c->getSymfonyVersion()); + $this->assertNull($c->getToken()); + $this->assertSame(extension_loaded('xdebug'), $c->hasXDebug()); + $this->assertSame(extension_loaded('Zend OPcache') && ini_get('opcache.enable'), $c->hasZendOpcache()); + $this->assertSame(extension_loaded('apcu') && ini_get('apc.enabled'), $c->hasApcu()); + } +} + +class KernelForTest extends Kernel +{ + public function getName() + { + return 'testkernel'; + } + + public function registerBundles() + { + } + + public function getBundles() + { + return array(); + } + + public function registerContainerConfiguration(LoaderInterface $loader) + { + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/Tests/DataCollector/DataCollectorTest.php b/lib/silex/vendor/symfony/http-kernel/Tests/DataCollector/DataCollectorTest.php new file mode 100644 index 000000000..54fd39e0d --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Tests/DataCollector/DataCollectorTest.php @@ -0,0 +1,38 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Tests\DataCollector; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\Tests\Fixtures\DataCollector\CloneVarDataCollector; +use Symfony\Component\VarDumper\Cloner\VarCloner; + +class DataCollectorTest extends TestCase +{ + public function testCloneVarStringWithScheme() + { + $c = new CloneVarDataCollector('scheme://foo'); + $c->collect(new Request(), new Response()); + $cloner = new VarCloner(); + + $this->assertEquals($cloner->cloneVar('scheme://foo'), $c->getData()); + } + + public function testCloneVarExistingFilePath() + { + $c = new CloneVarDataCollector(array($filePath = tempnam(sys_get_temp_dir(), 'clone_var_data_collector_'))); + $c->collect(new Request(), new Response()); + + $this->assertSame($filePath, $c->getData()[0]); + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/Tests/DataCollector/DumpDataCollectorTest.php b/lib/silex/vendor/symfony/http-kernel/Tests/DataCollector/DumpDataCollectorTest.php new file mode 100644 index 000000000..fc4b92b53 --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Tests/DataCollector/DumpDataCollectorTest.php @@ -0,0 +1,115 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Tests\DataCollector; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\HttpKernel\DataCollector\DumpDataCollector; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\VarDumper\Cloner\Data; + +/** + * @author Nicolas Grekas + */ +class DumpDataCollectorTest extends TestCase +{ + public function testDump() + { + $data = new Data(array(array(123))); + + $collector = new DumpDataCollector(); + + $this->assertSame('dump', $collector->getName()); + + $collector->dump($data); + $line = __LINE__ - 1; + $this->assertSame(1, $collector->getDumpsCount()); + + $dump = $collector->getDumps('html'); + $this->assertArrayHasKey('data', $dump[0]); + $dump[0]['data'] = preg_replace('/^.*?
 "
123\n
\n", + 'name' => 'DumpDataCollectorTest.php', + 'file' => __FILE__, + 'line' => $line, + 'fileExcerpt' => false, + ), + ); + $this->assertEquals($xDump, $dump); + + $this->assertStringMatchesFormat('a:3:{i:0;a:5:{s:4:"data";%c:39:"Symfony\Component\VarDumper\Cloner\Data":%a', $collector->serialize()); + $this->assertSame(0, $collector->getDumpsCount()); + $this->assertSame('a:2:{i:0;b:0;i:1;s:5:"UTF-8";}', $collector->serialize()); + } + + public function testCollectDefault() + { + $data = new Data(array(array(123))); + + $collector = new DumpDataCollector(); + + $collector->dump($data); + $line = __LINE__ - 1; + + ob_start(); + $collector->collect(new Request(), new Response()); + $output = ob_get_clean(); + + $this->assertSame("DumpDataCollectorTest.php on line {$line}:\n123\n", $output); + $this->assertSame(1, $collector->getDumpsCount()); + $collector->serialize(); + } + + public function testCollectHtml() + { + $data = new Data(array(array(123))); + + $collector = new DumpDataCollector(null, 'test://%f:%l'); + + $collector->dump($data); + $line = __LINE__ - 1; + $file = __FILE__; + $xOutput = <<DumpDataCollectorTest.php on line {$line}: +123 +
+EOTXT; + + ob_start(); + $response = new Response(); + $response->headers->set('Content-Type', 'text/html'); + $collector->collect(new Request(), $response); + $output = ob_get_clean(); + $output = preg_replace('#<(script|style).*?#s', '', $output); + $output = preg_replace('/sf-dump-\d+/', 'sf-dump', $output); + + $this->assertSame($xOutput, trim($output)); + $this->assertSame(1, $collector->getDumpsCount()); + $collector->serialize(); + } + + public function testFlush() + { + $data = new Data(array(array(456))); + $collector = new DumpDataCollector(); + $collector->dump($data); + $line = __LINE__ - 1; + + ob_start(); + $collector->__destruct(); + $this->assertSame("DumpDataCollectorTest.php on line {$line}:\n456\n", ob_get_clean()); + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/Tests/DataCollector/ExceptionDataCollectorTest.php b/lib/silex/vendor/symfony/http-kernel/Tests/DataCollector/ExceptionDataCollectorTest.php new file mode 100644 index 000000000..178f1f01a --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Tests/DataCollector/ExceptionDataCollectorTest.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\HttpKernel\Tests\DataCollector; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Debug\Exception\FlattenException; +use Symfony\Component\HttpKernel\DataCollector\ExceptionDataCollector; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +class ExceptionDataCollectorTest extends TestCase +{ + public function testCollect() + { + $e = new \Exception('foo', 500); + $c = new ExceptionDataCollector(); + $flattened = FlattenException::create($e); + $trace = $flattened->getTrace(); + + $this->assertFalse($c->hasException()); + + $c->collect(new Request(), new Response(), $e); + + $this->assertTrue($c->hasException()); + $this->assertEquals($flattened, $c->getException()); + $this->assertSame('foo', $c->getMessage()); + $this->assertSame(500, $c->getCode()); + $this->assertSame('exception', $c->getName()); + $this->assertSame($trace, $c->getTrace()); + } + + public function testCollectWithoutException() + { + $c = new ExceptionDataCollector(); + $c->collect(new Request(), new Response()); + + $this->assertFalse($c->hasException()); + } + + public function testReset() + { + $c = new ExceptionDataCollector(); + + $c->collect(new Request(), new Response(), new \Exception()); + $c->reset(); + $c->collect(new Request(), new Response()); + + $this->assertFalse($c->hasException()); + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/Tests/DataCollector/LoggerDataCollectorTest.php b/lib/silex/vendor/symfony/http-kernel/Tests/DataCollector/LoggerDataCollectorTest.php new file mode 100644 index 000000000..3dec3bd7f --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Tests/DataCollector/LoggerDataCollectorTest.php @@ -0,0 +1,144 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Tests\DataCollector; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Debug\Exception\SilencedErrorContext; +use Symfony\Component\HttpKernel\DataCollector\LoggerDataCollector; + +class LoggerDataCollectorTest extends TestCase +{ + public function testCollectWithUnexpectedFormat() + { + $logger = $this + ->getMockBuilder('Symfony\Component\HttpKernel\Log\DebugLoggerInterface') + ->setMethods(array('countErrors', 'getLogs', 'clear')) + ->getMock(); + $logger->expects($this->once())->method('countErrors')->will($this->returnValue('foo')); + $logger->expects($this->exactly(2))->method('getLogs')->will($this->returnValue(array())); + + $c = new LoggerDataCollector($logger, __DIR__.'/'); + $c->lateCollect(); + $compilerLogs = $c->getCompilerLogs()->getValue('message'); + + $this->assertSame(array( + array('message' => 'Removed service "Psr\Container\ContainerInterface"; reason: private alias.'), + array('message' => 'Removed service "Symfony\Component\DependencyInjection\ContainerInterface"; reason: private alias.'), + ), $compilerLogs['Symfony\Component\DependencyInjection\Compiler\RemovePrivateAliasesPass']); + + $this->assertSame(array( + array('message' => 'Some custom logging message'), + array('message' => 'With ending :'), + ), $compilerLogs['Unknown Compiler Pass']); + } + + /** + * @dataProvider getCollectTestData + */ + public function testCollect($nb, $logs, $expectedLogs, $expectedDeprecationCount, $expectedScreamCount, $expectedPriorities = null) + { + $logger = $this + ->getMockBuilder('Symfony\Component\HttpKernel\Log\DebugLoggerInterface') + ->setMethods(array('countErrors', 'getLogs', 'clear')) + ->getMock(); + $logger->expects($this->once())->method('countErrors')->will($this->returnValue($nb)); + $logger->expects($this->exactly(2))->method('getLogs')->will($this->returnValue($logs)); + + $c = new LoggerDataCollector($logger); + $c->lateCollect(); + + $this->assertEquals('logger', $c->getName()); + $this->assertEquals($nb, $c->countErrors()); + + $logs = array_map(function ($v) { + if (isset($v['context']['exception'])) { + $e = &$v['context']['exception']; + $e = isset($e["\0*\0message"]) ? array($e["\0*\0message"], $e["\0*\0severity"]) : array($e["\0Symfony\Component\Debug\Exception\SilencedErrorContext\0severity"]); + } + + return $v; + }, $c->getLogs()->getValue(true)); + $this->assertEquals($expectedLogs, $logs); + $this->assertEquals($expectedDeprecationCount, $c->countDeprecations()); + $this->assertEquals($expectedScreamCount, $c->countScreams()); + + if (isset($expectedPriorities)) { + $this->assertSame($expectedPriorities, $c->getPriorities()->getValue(true)); + } + } + + public function testReset() + { + $logger = $this + ->getMockBuilder('Symfony\Component\HttpKernel\Log\DebugLoggerInterface') + ->setMethods(array('countErrors', 'getLogs', 'clear')) + ->getMock(); + $logger->expects($this->once())->method('clear'); + + $c = new LoggerDataCollector($logger); + $c->reset(); + } + + public function getCollectTestData() + { + yield 'simple log' => array( + 1, + array(array('message' => 'foo', 'context' => array(), 'priority' => 100, 'priorityName' => 'DEBUG')), + array(array('message' => 'foo', 'context' => array(), 'priority' => 100, 'priorityName' => 'DEBUG')), + 0, + 0, + ); + + yield 'log with a context' => array( + 1, + array(array('message' => 'foo', 'context' => array('foo' => 'bar'), 'priority' => 100, 'priorityName' => 'DEBUG')), + array(array('message' => 'foo', 'context' => array('foo' => 'bar'), 'priority' => 100, 'priorityName' => 'DEBUG')), + 0, + 0, + ); + + if (!class_exists(SilencedErrorContext::class)) { + return; + } + + yield 'logs with some deprecations' => array( + 1, + array( + array('message' => 'foo3', 'context' => array('exception' => new \ErrorException('warning', 0, E_USER_WARNING)), 'priority' => 100, 'priorityName' => 'DEBUG'), + array('message' => 'foo', 'context' => array('exception' => new \ErrorException('deprecated', 0, E_DEPRECATED)), 'priority' => 100, 'priorityName' => 'DEBUG'), + array('message' => 'foo2', 'context' => array('exception' => new \ErrorException('deprecated', 0, E_USER_DEPRECATED)), 'priority' => 100, 'priorityName' => 'DEBUG'), + ), + array( + array('message' => 'foo3', 'context' => array('exception' => array('warning', E_USER_WARNING)), 'priority' => 100, 'priorityName' => 'DEBUG'), + array('message' => 'foo', 'context' => array('exception' => array('deprecated', E_DEPRECATED)), 'priority' => 100, 'priorityName' => 'DEBUG', 'errorCount' => 1, 'scream' => false), + array('message' => 'foo2', 'context' => array('exception' => array('deprecated', E_USER_DEPRECATED)), 'priority' => 100, 'priorityName' => 'DEBUG', 'errorCount' => 1, 'scream' => false), + ), + 2, + 0, + array(100 => array('count' => 3, 'name' => 'DEBUG')), + ); + + yield 'logs with some silent errors' => array( + 1, + array( + array('message' => 'foo3', 'context' => array('exception' => new \ErrorException('warning', 0, E_USER_WARNING)), 'priority' => 100, 'priorityName' => 'DEBUG'), + array('message' => 'foo3', 'context' => array('exception' => new SilencedErrorContext(E_USER_WARNING, __FILE__, __LINE__)), 'priority' => 100, 'priorityName' => 'DEBUG'), + ), + array( + array('message' => 'foo3', 'context' => array('exception' => array('warning', E_USER_WARNING)), 'priority' => 100, 'priorityName' => 'DEBUG'), + array('message' => 'foo3', 'context' => array('exception' => array(E_USER_WARNING)), 'priority' => 100, 'priorityName' => 'DEBUG', 'errorCount' => 1, 'scream' => true), + ), + 0, + 1, + ); + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/Tests/DataCollector/MemoryDataCollectorTest.php b/lib/silex/vendor/symfony/http-kernel/Tests/DataCollector/MemoryDataCollectorTest.php new file mode 100644 index 000000000..ab78e9e8e --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Tests/DataCollector/MemoryDataCollectorTest.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\HttpKernel\Tests\DataCollector; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\HttpKernel\DataCollector\MemoryDataCollector; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +class MemoryDataCollectorTest extends TestCase +{ + public function testCollect() + { + $collector = new MemoryDataCollector(); + $collector->collect(new Request(), new Response()); + + $this->assertInternalType('integer', $collector->getMemory()); + $this->assertInternalType('integer', $collector->getMemoryLimit()); + $this->assertSame('memory', $collector->getName()); + } + + /** @dataProvider getBytesConversionTestData */ + public function testBytesConversion($limit, $bytes) + { + $collector = new MemoryDataCollector(); + $method = new \ReflectionMethod($collector, 'convertToBytes'); + $method->setAccessible(true); + $this->assertEquals($bytes, $method->invoke($collector, $limit)); + } + + public function getBytesConversionTestData() + { + return array( + array('2k', 2048), + array('2 k', 2048), + array('8m', 8 * 1024 * 1024), + array('+2 k', 2048), + array('+2???k', 2048), + array('0x10', 16), + array('0xf', 15), + array('010', 8), + array('+0x10 k', 16 * 1024), + array('1g', 1024 * 1024 * 1024), + array('1G', 1024 * 1024 * 1024), + array('-1', -1), + array('0', 0), + array('2mk', 2048), // the unit must be the last char, so in this case 'k', not 'm' + ); + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/Tests/DataCollector/RequestDataCollectorTest.php b/lib/silex/vendor/symfony/http-kernel/Tests/DataCollector/RequestDataCollectorTest.php new file mode 100644 index 000000000..69bef76d4 --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Tests/DataCollector/RequestDataCollectorTest.php @@ -0,0 +1,334 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Tests\DataCollector; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\HttpFoundation\ParameterBag; +use Symfony\Component\HttpFoundation\RedirectResponse; +use Symfony\Component\HttpFoundation\Session\Session; +use Symfony\Component\HttpFoundation\Session\Storage\MockArraySessionStorage; +use Symfony\Component\HttpKernel\Controller\ArgumentResolverInterface; +use Symfony\Component\HttpKernel\Event\FilterResponseEvent; +use Symfony\Component\HttpKernel\HttpKernel; +use Symfony\Component\HttpKernel\HttpKernelInterface; +use Symfony\Component\HttpKernel\DataCollector\RequestDataCollector; +use Symfony\Component\HttpKernel\Event\FilterControllerEvent; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpFoundation\Cookie; +use Symfony\Component\EventDispatcher\EventDispatcher; + +class RequestDataCollectorTest extends TestCase +{ + public function testCollect() + { + $c = new RequestDataCollector(); + + $c->collect($request = $this->createRequest(), $this->createResponse()); + $c->lateCollect(); + + $attributes = $c->getRequestAttributes(); + + $this->assertSame('request', $c->getName()); + $this->assertInstanceOf('Symfony\Component\HttpFoundation\ParameterBag', $c->getRequestHeaders()); + $this->assertInstanceOf('Symfony\Component\HttpFoundation\ParameterBag', $c->getRequestServer()); + $this->assertInstanceOf('Symfony\Component\HttpFoundation\ParameterBag', $c->getRequestCookies()); + $this->assertInstanceOf('Symfony\Component\HttpFoundation\ParameterBag', $attributes); + $this->assertInstanceOf('Symfony\Component\HttpFoundation\ParameterBag', $c->getRequestRequest()); + $this->assertInstanceOf('Symfony\Component\HttpFoundation\ParameterBag', $c->getRequestQuery()); + $this->assertInstanceOf(ParameterBag::class, $c->getResponseCookies()); + $this->assertSame('html', $c->getFormat()); + $this->assertEquals('foobar', $c->getRoute()); + $this->assertEquals(array('name' => 'foo'), $c->getRouteParams()); + $this->assertSame(array(), $c->getSessionAttributes()); + $this->assertSame('en', $c->getLocale()); + $this->assertContains(__FILE__, $attributes->get('resource')); + $this->assertSame('stdClass', $attributes->get('object')->getType()); + + $this->assertInstanceOf('Symfony\Component\HttpFoundation\ParameterBag', $c->getResponseHeaders()); + $this->assertSame('OK', $c->getStatusText()); + $this->assertSame(200, $c->getStatusCode()); + $this->assertSame('application/json', $c->getContentType()); + } + + public function testCollectWithoutRouteParams() + { + $request = $this->createRequest(array()); + + $c = new RequestDataCollector(); + $c->collect($request, $this->createResponse()); + $c->lateCollect(); + + $this->assertEquals(array(), $c->getRouteParams()); + } + + /** + * @dataProvider provideControllerCallables + */ + public function testControllerInspection($name, $callable, $expected) + { + $c = new RequestDataCollector(); + $request = $this->createRequest(); + $response = $this->createResponse(); + $this->injectController($c, $callable, $request); + $c->collect($request, $response); + $c->lateCollect(); + + $this->assertSame($expected, $c->getController()->getValue(true), sprintf('Testing: %s', $name)); + } + + public function provideControllerCallables() + { + // make sure we always match the line number + $r1 = new \ReflectionMethod($this, 'testControllerInspection'); + $r2 = new \ReflectionMethod($this, 'staticControllerMethod'); + $r3 = new \ReflectionClass($this); + + // test name, callable, expected + return array( + array( + '"Regular" callable', + array($this, 'testControllerInspection'), + array( + 'class' => __NAMESPACE__.'\RequestDataCollectorTest', + 'method' => 'testControllerInspection', + 'file' => __FILE__, + 'line' => $r1->getStartLine(), + ), + ), + + array( + 'Closure', + function () { return 'foo'; }, + array( + 'class' => __NAMESPACE__.'\{closure}', + 'method' => null, + 'file' => __FILE__, + 'line' => __LINE__ - 5, + ), + ), + + array( + 'Static callback as string', + __NAMESPACE__.'\RequestDataCollectorTest::staticControllerMethod', + array( + 'class' => 'Symfony\Component\HttpKernel\Tests\DataCollector\RequestDataCollectorTest', + 'method' => 'staticControllerMethod', + 'file' => __FILE__, + 'line' => $r2->getStartLine(), + ), + ), + + array( + 'Static callable with instance', + array($this, 'staticControllerMethod'), + array( + 'class' => 'Symfony\Component\HttpKernel\Tests\DataCollector\RequestDataCollectorTest', + 'method' => 'staticControllerMethod', + 'file' => __FILE__, + 'line' => $r2->getStartLine(), + ), + ), + + array( + 'Static callable with class name', + array('Symfony\Component\HttpKernel\Tests\DataCollector\RequestDataCollectorTest', 'staticControllerMethod'), + array( + 'class' => 'Symfony\Component\HttpKernel\Tests\DataCollector\RequestDataCollectorTest', + 'method' => 'staticControllerMethod', + 'file' => __FILE__, + 'line' => $r2->getStartLine(), + ), + ), + + array( + 'Callable with instance depending on __call()', + array($this, 'magicMethod'), + array( + 'class' => 'Symfony\Component\HttpKernel\Tests\DataCollector\RequestDataCollectorTest', + 'method' => 'magicMethod', + 'file' => 'n/a', + 'line' => 'n/a', + ), + ), + + array( + 'Callable with class name depending on __callStatic()', + array('Symfony\Component\HttpKernel\Tests\DataCollector\RequestDataCollectorTest', 'magicMethod'), + array( + 'class' => 'Symfony\Component\HttpKernel\Tests\DataCollector\RequestDataCollectorTest', + 'method' => 'magicMethod', + 'file' => 'n/a', + 'line' => 'n/a', + ), + ), + + array( + 'Invokable controller', + $this, + array( + 'class' => 'Symfony\Component\HttpKernel\Tests\DataCollector\RequestDataCollectorTest', + 'method' => null, + 'file' => __FILE__, + 'line' => $r3->getStartLine(), + ), + ), + ); + } + + public function testItIgnoresInvalidCallables() + { + $request = $this->createRequestWithSession(); + $response = new RedirectResponse('/'); + + $c = new RequestDataCollector(); + $c->collect($request, $response); + + $this->assertSame('n/a', $c->getController()); + } + + public function testItAddsRedirectedAttributesWhenRequestContainsSpecificCookie() + { + $request = $this->createRequest(); + $request->cookies->add(array( + 'sf_redirect' => '{}', + )); + + $kernel = $this->getMockBuilder(HttpKernelInterface::class)->getMock(); + + $c = new RequestDataCollector(); + $c->onKernelResponse(new FilterResponseEvent($kernel, $request, HttpKernelInterface::MASTER_REQUEST, $this->createResponse())); + + $this->assertTrue($request->attributes->get('_redirected')); + } + + public function testItSetsARedirectCookieIfTheResponseIsARedirection() + { + $c = new RequestDataCollector(); + + $response = $this->createResponse(); + $response->setStatusCode(302); + $response->headers->set('Location', '/somewhere-else'); + + $c->collect($request = $this->createRequest(), $response); + $c->lateCollect(); + + $cookie = $this->getCookieByName($response, 'sf_redirect'); + + $this->assertNotEmpty($cookie->getValue()); + } + + public function testItCollectsTheRedirectionAndClearTheCookie() + { + $c = new RequestDataCollector(); + + $request = $this->createRequest(); + $request->attributes->set('_redirected', true); + $request->cookies->add(array( + 'sf_redirect' => '{"method": "POST"}', + )); + + $c->collect($request, $response = $this->createResponse()); + $c->lateCollect(); + + $this->assertEquals('POST', $c->getRedirect()['method']); + + $cookie = $this->getCookieByName($response, 'sf_redirect'); + $this->assertNull($cookie->getValue()); + } + + protected function createRequest($routeParams = array('name' => 'foo')) + { + $request = Request::create('http://test.com/foo?bar=baz'); + $request->attributes->set('foo', 'bar'); + $request->attributes->set('_route', 'foobar'); + $request->attributes->set('_route_params', $routeParams); + $request->attributes->set('resource', fopen(__FILE__, 'r')); + $request->attributes->set('object', new \stdClass()); + + return $request; + } + + private function createRequestWithSession() + { + $request = $this->createRequest(); + $request->attributes->set('_controller', 'Foo::bar'); + $request->setSession(new Session(new MockArraySessionStorage())); + $request->getSession()->start(); + + return $request; + } + + protected function createResponse() + { + $response = new Response(); + $response->setStatusCode(200); + $response->headers->set('Content-Type', 'application/json'); + $response->headers->set('X-Foo-Bar', null); + $response->headers->setCookie(new Cookie('foo', 'bar', 1, '/foo', 'localhost', true, true)); + $response->headers->setCookie(new Cookie('bar', 'foo', new \DateTime('@946684800'))); + $response->headers->setCookie(new Cookie('bazz', 'foo', '2000-12-12')); + + return $response; + } + + /** + * Inject the given controller callable into the data collector. + */ + protected function injectController($collector, $controller, $request) + { + $resolver = $this->getMockBuilder('Symfony\\Component\\HttpKernel\\Controller\\ControllerResolverInterface')->getMock(); + $httpKernel = new HttpKernel(new EventDispatcher(), $resolver, null, $this->getMockBuilder(ArgumentResolverInterface::class)->getMock()); + $event = new FilterControllerEvent($httpKernel, $controller, $request, HttpKernelInterface::MASTER_REQUEST); + $collector->onKernelController($event); + } + + /** + * Dummy method used as controller callable. + */ + public static function staticControllerMethod() + { + throw new \LogicException('Unexpected method call'); + } + + /** + * Magic method to allow non existing methods to be called and delegated. + */ + public function __call($method, $args) + { + throw new \LogicException('Unexpected method call'); + } + + /** + * Magic method to allow non existing methods to be called and delegated. + */ + public static function __callStatic($method, $args) + { + throw new \LogicException('Unexpected method call'); + } + + public function __invoke() + { + throw new \LogicException('Unexpected method call'); + } + + private function getCookieByName(Response $response, $name) + { + foreach ($response->headers->getCookies() as $cookie) { + if ($cookie->getName() == $name) { + return $cookie; + } + } + + throw new \InvalidArgumentException(sprintf('Cookie named "%s" is not in response', $name)); + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/Tests/DataCollector/TimeDataCollectorTest.php b/lib/silex/vendor/symfony/http-kernel/Tests/DataCollector/TimeDataCollectorTest.php new file mode 100644 index 000000000..8048cc37a --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Tests/DataCollector/TimeDataCollectorTest.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\HttpKernel\Tests\DataCollector; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\HttpKernel\DataCollector\TimeDataCollector; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +/** + * @group time-sensitive + */ +class TimeDataCollectorTest extends TestCase +{ + public function testCollect() + { + $c = new TimeDataCollector(); + + $request = new Request(); + $request->server->set('REQUEST_TIME', 1); + + $c->collect($request, new Response()); + + $this->assertEquals(0, $c->getStartTime()); + + $request->server->set('REQUEST_TIME_FLOAT', 2); + + $c->collect($request, new Response()); + + $this->assertEquals(2000, $c->getStartTime()); + + $request = new Request(); + $c->collect($request, new Response()); + $this->assertEquals(0, $c->getStartTime()); + + $kernel = $this->getMockBuilder('Symfony\Component\HttpKernel\KernelInterface')->getMock(); + $kernel->expects($this->once())->method('getStartTime')->will($this->returnValue(123456)); + + $c = new TimeDataCollector($kernel); + $request = new Request(); + $request->server->set('REQUEST_TIME', 1); + + $c->collect($request, new Response()); + $this->assertEquals(123456000, $c->getStartTime()); + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/Tests/DataCollector/Util/ValueExporterTest.php b/lib/silex/vendor/symfony/http-kernel/Tests/DataCollector/Util/ValueExporterTest.php new file mode 100644 index 000000000..5fe92d60e --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Tests/DataCollector/Util/ValueExporterTest.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\HttpKernel\Tests\DataCollector\Util; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\HttpKernel\DataCollector\Util\ValueExporter; + +/** + * @group legacy + */ +class ValueExporterTest extends TestCase +{ + /** + * @var ValueExporter + */ + private $valueExporter; + + protected function setUp() + { + $this->valueExporter = new ValueExporter(); + } + + public function testDateTime() + { + $dateTime = new \DateTime('2014-06-10 07:35:40', new \DateTimeZone('UTC')); + $this->assertSame('Object(DateTime) - 2014-06-10T07:35:40+00:00', $this->valueExporter->exportValue($dateTime)); + } + + public function testDateTimeImmutable() + { + $dateTime = new \DateTimeImmutable('2014-06-10 07:35:40', new \DateTimeZone('UTC')); + $this->assertSame('Object(DateTimeImmutable) - 2014-06-10T07:35:40+00:00', $this->valueExporter->exportValue($dateTime)); + } + + public function testIncompleteClass() + { + $foo = new \__PHP_Incomplete_Class(); + $array = new \ArrayObject($foo); + $array['__PHP_Incomplete_Class_Name'] = 'AppBundle/Foo'; + $this->assertSame('__PHP_Incomplete_Class(AppBundle/Foo)', $this->valueExporter->exportValue($foo)); + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/Tests/Debug/FileLinkFormatterTest.php b/lib/silex/vendor/symfony/http-kernel/Tests/Debug/FileLinkFormatterTest.php new file mode 100644 index 000000000..d616098a5 --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Tests/Debug/FileLinkFormatterTest.php @@ -0,0 +1,67 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Tests\Debug; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\RequestStack; +use Symfony\Component\HttpKernel\Debug\FileLinkFormatter; + +class FileLinkFormatterTest extends TestCase +{ + public function testWhenNoFileLinkFormatAndNoRequest() + { + $sut = new FileLinkFormatter(); + + $this->assertFalse($sut->format('/kernel/root/src/my/very/best/file.php', 3)); + } + + public function testWhenFileLinkFormatAndNoRequest() + { + $file = __DIR__.DIRECTORY_SEPARATOR.'file.php'; + + $sut = new FileLinkFormatter('debug://open?url=file://%f&line=%l', new RequestStack()); + + $this->assertSame("debug://open?url=file://$file&line=3", $sut->format($file, 3)); + } + + public function testWhenFileLinkFormatAndRequest() + { + $file = __DIR__.DIRECTORY_SEPARATOR.'file.php'; + $baseDir = __DIR__; + $requestStack = new RequestStack(); + $request = new Request(); + $requestStack->push($request); + + $sut = new FileLinkFormatter('debug://open?url=file://%f&line=%l', $requestStack, __DIR__, '/_profiler/open?file=%f&line=%l#line%l'); + + $this->assertSame("debug://open?url=file://$file&line=3", $sut->format($file, 3)); + } + + public function testWhenNoFileLinkFormatAndRequest() + { + $file = __DIR__.DIRECTORY_SEPARATOR.'file.php'; + $requestStack = new RequestStack(); + $request = new Request(); + $requestStack->push($request); + + $request->server->set('SERVER_NAME', 'www.example.org'); + $request->server->set('SERVER_PORT', 80); + $request->server->set('SCRIPT_NAME', '/app.php'); + $request->server->set('SCRIPT_FILENAME', '/web/app.php'); + $request->server->set('REQUEST_URI', '/app.php/example'); + + $sut = new FileLinkFormatter(null, $requestStack, __DIR__, '/_profiler/open?file=%f&line=%l#line%l'); + + $this->assertSame('http://www.example.org/app.php/_profiler/open?file=file.php&line=3#line3', $sut->format($file, 3)); + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/Tests/Debug/TraceableEventDispatcherTest.php b/lib/silex/vendor/symfony/http-kernel/Tests/Debug/TraceableEventDispatcherTest.php new file mode 100644 index 000000000..aaa82d524 --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Tests/Debug/TraceableEventDispatcherTest.php @@ -0,0 +1,121 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Tests\Debug; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\EventDispatcher\EventDispatcher; +use Symfony\Component\HttpFoundation\RequestStack; +use Symfony\Component\HttpKernel\Debug\TraceableEventDispatcher; +use Symfony\Component\HttpKernel\HttpKernel; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\Stopwatch\Stopwatch; + +class TraceableEventDispatcherTest extends TestCase +{ + public function testStopwatchSections() + { + $dispatcher = new TraceableEventDispatcher(new EventDispatcher(), $stopwatch = new Stopwatch()); + $kernel = $this->getHttpKernel($dispatcher, function () { return new Response(); }); + $request = Request::create('/'); + $response = $kernel->handle($request); + $kernel->terminate($request, $response); + + $events = $stopwatch->getSectionEvents($response->headers->get('X-Debug-Token')); + $this->assertEquals(array( + '__section__', + 'kernel.request', + 'kernel.controller', + 'kernel.controller_arguments', + 'controller', + 'kernel.response', + 'kernel.terminate', + ), array_keys($events)); + } + + public function testStopwatchCheckControllerOnRequestEvent() + { + $stopwatch = $this->getMockBuilder('Symfony\Component\Stopwatch\Stopwatch') + ->setMethods(array('isStarted')) + ->getMock(); + $stopwatch->expects($this->once()) + ->method('isStarted') + ->will($this->returnValue(false)); + + $dispatcher = new TraceableEventDispatcher(new EventDispatcher(), $stopwatch); + + $kernel = $this->getHttpKernel($dispatcher, function () { return new Response(); }); + $request = Request::create('/'); + $kernel->handle($request); + } + + public function testStopwatchStopControllerOnRequestEvent() + { + $stopwatch = $this->getMockBuilder('Symfony\Component\Stopwatch\Stopwatch') + ->setMethods(array('isStarted', 'stop', 'stopSection')) + ->getMock(); + $stopwatch->expects($this->once()) + ->method('isStarted') + ->will($this->returnValue(true)); + $stopwatch->expects($this->once()) + ->method('stop'); + $stopwatch->expects($this->once()) + ->method('stopSection'); + + $dispatcher = new TraceableEventDispatcher(new EventDispatcher(), $stopwatch); + + $kernel = $this->getHttpKernel($dispatcher, function () { return new Response(); }); + $request = Request::create('/'); + $kernel->handle($request); + } + + public function testAddListenerNested() + { + $called1 = false; + $called2 = false; + $dispatcher = new TraceableEventDispatcher(new EventDispatcher(), new Stopwatch()); + $dispatcher->addListener('my-event', function () use ($dispatcher, &$called1, &$called2) { + $called1 = true; + $dispatcher->addListener('my-event', function () use (&$called2) { + $called2 = true; + }); + }); + $dispatcher->dispatch('my-event'); + $this->assertTrue($called1); + $this->assertFalse($called2); + $dispatcher->dispatch('my-event'); + $this->assertTrue($called2); + } + + public function testListenerCanRemoveItselfWhenExecuted() + { + $eventDispatcher = new TraceableEventDispatcher(new EventDispatcher(), new Stopwatch()); + $listener1 = function () use ($eventDispatcher, &$listener1) { + $eventDispatcher->removeListener('foo', $listener1); + }; + $eventDispatcher->addListener('foo', $listener1); + $eventDispatcher->addListener('foo', function () {}); + $eventDispatcher->dispatch('foo'); + + $this->assertCount(1, $eventDispatcher->getListeners('foo'), 'expected listener1 to be removed'); + } + + protected function getHttpKernel($dispatcher, $controller) + { + $controllerResolver = $this->getMockBuilder('Symfony\Component\HttpKernel\Controller\ControllerResolverInterface')->getMock(); + $controllerResolver->expects($this->once())->method('getController')->will($this->returnValue($controller)); + $argumentResolver = $this->getMockBuilder('Symfony\Component\HttpKernel\Controller\ArgumentResolverInterface')->getMock(); + $argumentResolver->expects($this->once())->method('getArguments')->will($this->returnValue(array())); + + return new HttpKernel($dispatcher, $controllerResolver, new RequestStack(), $argumentResolver); + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/Tests/DependencyInjection/AddAnnotatedClassesToCachePassTest.php b/lib/silex/vendor/symfony/http-kernel/Tests/DependencyInjection/AddAnnotatedClassesToCachePassTest.php new file mode 100644 index 000000000..a2fb6afca --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Tests/DependencyInjection/AddAnnotatedClassesToCachePassTest.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\HttpKernel\Tests\DependencyInjection; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\HttpKernel\DependencyInjection\AddAnnotatedClassesToCachePass; + +class AddAnnotatedClassesToCachePassTest extends TestCase +{ + public function testExpandClasses() + { + $r = new \ReflectionClass(AddAnnotatedClassesToCachePass::class); + $pass = $r->newInstanceWithoutConstructor(); + $r = new \ReflectionMethod(AddAnnotatedClassesToCachePass::class, 'expandClasses'); + $r->setAccessible(true); + $expand = $r->getClosure($pass); + + $this->assertSame('Foo', $expand(array('Foo'), array())[0]); + $this->assertSame('Foo', $expand(array('\\Foo'), array())[0]); + $this->assertSame('Foo', $expand(array('Foo'), array('\\Foo'))[0]); + $this->assertSame('Foo', $expand(array('Foo'), array('Foo'))[0]); + $this->assertSame('Foo', $expand(array('\\Foo'), array('\\Foo\\Bar'))[0]); + $this->assertSame('Foo', $expand(array('Foo'), array('\\Foo\\Bar'))[0]); + $this->assertSame('Foo', $expand(array('\\Foo'), array('\\Foo\\Bar\\Acme'))[0]); + + $this->assertSame('Foo\\Bar', $expand(array('Foo\\'), array('\\Foo\\Bar'))[0]); + $this->assertSame('Foo\\Bar\\Acme', $expand(array('Foo\\'), array('\\Foo\\Bar\\Acme'))[0]); + $this->assertEmpty($expand(array('Foo\\'), array('\\Foo'))); + + $this->assertSame('Acme\\Foo\\Bar', $expand(array('**\\Foo\\'), array('\\Acme\\Foo\\Bar'))[0]); + $this->assertEmpty($expand(array('**\\Foo\\'), array('\\Foo\\Bar'))); + $this->assertEmpty($expand(array('**\\Foo\\'), array('\\Acme\\Foo'))); + $this->assertEmpty($expand(array('**\\Foo\\'), array('\\Foo'))); + + $this->assertSame('Acme\\Foo', $expand(array('**\\Foo'), array('\\Acme\\Foo'))[0]); + $this->assertEmpty($expand(array('**\\Foo'), array('\\Acme\\Foo\\AcmeBundle'))); + $this->assertEmpty($expand(array('**\\Foo'), array('\\Acme\\FooBar\\AcmeBundle'))); + + $this->assertSame('Foo\\Acme\\Bar', $expand(array('Foo\\*\\Bar'), array('\\Foo\\Acme\\Bar'))[0]); + $this->assertEmpty($expand(array('Foo\\*\\Bar'), array('\\Foo\\Acme\\Bundle\\Bar'))); + + $this->assertSame('Foo\\Acme\\Bar', $expand(array('Foo\\**\\Bar'), array('\\Foo\\Acme\\Bar'))[0]); + $this->assertSame('Foo\\Acme\\Bundle\\Bar', $expand(array('Foo\\**\\Bar'), array('\\Foo\\Acme\\Bundle\\Bar'))[0]); + + $this->assertSame('Acme\\Bar', $expand(array('*\\Bar'), array('\\Acme\\Bar'))[0]); + $this->assertEmpty($expand(array('*\\Bar'), array('\\Bar'))); + $this->assertEmpty($expand(array('*\\Bar'), array('\\Foo\\Acme\\Bar'))); + + $this->assertSame('Foo\\Acme\\Bar', $expand(array('**\\Bar'), array('\\Foo\\Acme\\Bar'))[0]); + $this->assertSame('Foo\\Acme\\Bundle\\Bar', $expand(array('**\\Bar'), array('\\Foo\\Acme\\Bundle\\Bar'))[0]); + $this->assertEmpty($expand(array('**\\Bar'), array('\\Bar'))); + + $this->assertSame('Foo\\Bar', $expand(array('Foo\\*'), array('\\Foo\\Bar'))[0]); + $this->assertEmpty($expand(array('Foo\\*'), array('\\Foo\\Acme\\Bar'))); + + $this->assertSame('Foo\\Bar', $expand(array('Foo\\**'), array('\\Foo\\Bar'))[0]); + $this->assertSame('Foo\\Acme\\Bar', $expand(array('Foo\\**'), array('\\Foo\\Acme\\Bar'))[0]); + + $this->assertSame(array('Foo\\Bar'), $expand(array('Foo\\*'), array('Foo\\Bar', 'Foo\\BarTest'))); + $this->assertSame(array('Foo\\Bar', 'Foo\\BarTest'), $expand(array('Foo\\*', 'Foo\\*Test'), array('Foo\\Bar', 'Foo\\BarTest'))); + + $this->assertSame( + 'Acme\\FooBundle\\Controller\\DefaultController', + $expand(array('**Bundle\\Controller\\'), array('\\Acme\\FooBundle\\Controller\\DefaultController'))[0] + ); + + $this->assertSame( + 'FooBundle\\Controller\\DefaultController', + $expand(array('**Bundle\\Controller\\'), array('\\FooBundle\\Controller\\DefaultController'))[0] + ); + + $this->assertSame( + 'Acme\\FooBundle\\Controller\\Bar\\DefaultController', + $expand(array('**Bundle\\Controller\\'), array('\\Acme\\FooBundle\\Controller\\Bar\\DefaultController'))[0] + ); + + $this->assertSame( + 'Bundle\\Controller\\Bar\\DefaultController', + $expand(array('**Bundle\\Controller\\'), array('\\Bundle\\Controller\\Bar\\DefaultController'))[0] + ); + + $this->assertSame( + 'Acme\\Bundle\\Controller\\Bar\\DefaultController', + $expand(array('**Bundle\\Controller\\'), array('\\Acme\\Bundle\\Controller\\Bar\\DefaultController'))[0] + ); + + $this->assertSame('Foo\\Bar', $expand(array('Foo\\Bar'), array())[0]); + $this->assertSame('Foo\\Acme\\Bar', $expand(array('Foo\\**'), array('\\Foo\\Acme\\Bar'))[0]); + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/Tests/DependencyInjection/ControllerArgumentValueResolverPassTest.php b/lib/silex/vendor/symfony/http-kernel/Tests/DependencyInjection/ControllerArgumentValueResolverPassTest.php new file mode 100644 index 000000000..df8977de0 --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Tests/DependencyInjection/ControllerArgumentValueResolverPassTest.php @@ -0,0 +1,67 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Tests\DependencyInjection; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\HttpKernel\Controller\ArgumentResolver; +use Symfony\Component\HttpKernel\DependencyInjection\ControllerArgumentValueResolverPass; + +class ControllerArgumentValueResolverPassTest extends TestCase +{ + public function testServicesAreOrderedAccordingToPriority() + { + $services = array( + 'n3' => array(array()), + 'n1' => array(array('priority' => 200)), + 'n2' => array(array('priority' => 100)), + ); + + $expected = array( + new Reference('n1'), + new Reference('n2'), + new Reference('n3'), + ); + + $definition = new Definition(ArgumentResolver::class, array(null, array())); + $container = new ContainerBuilder(); + $container->setDefinition('argument_resolver', $definition); + + foreach ($services as $id => list($tag)) { + $container->register($id)->addTag('controller.argument_value_resolver', $tag); + } + + (new ControllerArgumentValueResolverPass())->process($container); + $this->assertEquals($expected, $definition->getArgument(1)->getValues()); + } + + public function testReturningEmptyArrayWhenNoService() + { + $definition = new Definition(ArgumentResolver::class, array(null, array())); + $container = new ContainerBuilder(); + $container->setDefinition('argument_resolver', $definition); + + (new ControllerArgumentValueResolverPass())->process($container); + $this->assertEquals(array(), $definition->getArgument(1)->getValues()); + } + + public function testNoArgumentResolver() + { + $container = new ContainerBuilder(); + + (new ControllerArgumentValueResolverPass())->process($container); + + $this->assertFalse($container->hasDefinition('argument_resolver')); + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/Tests/DependencyInjection/FragmentRendererPassTest.php b/lib/silex/vendor/symfony/http-kernel/Tests/DependencyInjection/FragmentRendererPassTest.php new file mode 100644 index 000000000..d28c6eca5 --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Tests/DependencyInjection/FragmentRendererPassTest.php @@ -0,0 +1,105 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Tests\DependencyInjection; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpKernel\DependencyInjection\FragmentRendererPass; +use Symfony\Component\HttpKernel\Fragment\FragmentRendererInterface; + +class FragmentRendererPassTest extends TestCase +{ + /** + * Tests that content rendering not implementing FragmentRendererInterface + * trigger an exception. + * + * @expectedException \InvalidArgumentException + */ + public function testContentRendererWithoutInterface() + { + // one service, not implementing any interface + $services = array( + 'my_content_renderer' => array(array('alias' => 'foo')), + ); + + $definition = $this->getMockBuilder('Symfony\Component\DependencyInjection\Definition')->getMock(); + + $builder = $this->getMockBuilder('Symfony\Component\DependencyInjection\ContainerBuilder')->setMethods(array('hasDefinition', 'findTaggedServiceIds', 'getDefinition'))->getMock(); + $builder->expects($this->any()) + ->method('hasDefinition') + ->will($this->returnValue(true)); + + // We don't test kernel.fragment_renderer here + $builder->expects($this->atLeastOnce()) + ->method('findTaggedServiceIds') + ->will($this->returnValue($services)); + + $builder->expects($this->atLeastOnce()) + ->method('getDefinition') + ->will($this->returnValue($definition)); + + $pass = new FragmentRendererPass(); + $pass->process($builder); + } + + public function testValidContentRenderer() + { + $services = array( + 'my_content_renderer' => array(array('alias' => 'foo')), + ); + + $renderer = new Definition('', array(null)); + + $definition = $this->getMockBuilder('Symfony\Component\DependencyInjection\Definition')->getMock(); + $definition->expects($this->atLeastOnce()) + ->method('getClass') + ->will($this->returnValue('Symfony\Component\HttpKernel\Tests\DependencyInjection\RendererService')); + + $builder = $this->getMockBuilder('Symfony\Component\DependencyInjection\ContainerBuilder')->setMethods(array('hasDefinition', 'findTaggedServiceIds', 'getDefinition', 'getReflectionClass'))->getMock(); + $builder->expects($this->any()) + ->method('hasDefinition') + ->will($this->returnValue(true)); + + // We don't test kernel.fragment_renderer here + $builder->expects($this->atLeastOnce()) + ->method('findTaggedServiceIds') + ->will($this->returnValue($services)); + + $builder->expects($this->atLeastOnce()) + ->method('getDefinition') + ->will($this->onConsecutiveCalls($renderer, $definition)); + + $builder->expects($this->atLeastOnce()) + ->method('getReflectionClass') + ->with('Symfony\Component\HttpKernel\Tests\DependencyInjection\RendererService') + ->will($this->returnValue(new \ReflectionClass('Symfony\Component\HttpKernel\Tests\DependencyInjection\RendererService'))); + + $pass = new FragmentRendererPass(); + $pass->process($builder); + + $this->assertInstanceOf(Reference::class, $renderer->getArgument(0)); + } +} + +class RendererService implements FragmentRendererInterface +{ + public function render($uri, Request $request = null, array $options = array()) + { + } + + public function getName() + { + return 'test'; + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/Tests/DependencyInjection/LazyLoadingFragmentHandlerTest.php b/lib/silex/vendor/symfony/http-kernel/Tests/DependencyInjection/LazyLoadingFragmentHandlerTest.php new file mode 100644 index 000000000..730214837 --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Tests/DependencyInjection/LazyLoadingFragmentHandlerTest.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\HttpKernel\Tests\DependencyInjection; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\HttpKernel\DependencyInjection\LazyLoadingFragmentHandler; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +class LazyLoadingFragmentHandlerTest extends TestCase +{ + /** + * @group legacy + * @expectedDeprecation The Symfony\Component\HttpKernel\DependencyInjection\LazyLoadingFragmentHandler::addRendererService() method is deprecated since Symfony 3.3 and will be removed in 4.0. + */ + public function testRenderWithLegacyMapping() + { + $renderer = $this->getMockBuilder('Symfony\Component\HttpKernel\Fragment\FragmentRendererInterface')->getMock(); + $renderer->expects($this->once())->method('getName')->will($this->returnValue('foo')); + $renderer->expects($this->any())->method('render')->will($this->returnValue(new Response())); + + $requestStack = $this->getMockBuilder('Symfony\Component\HttpFoundation\RequestStack')->getMock(); + $requestStack->expects($this->any())->method('getCurrentRequest')->will($this->returnValue(Request::create('/'))); + + $container = $this->getMockBuilder('Symfony\Component\DependencyInjection\ContainerInterface')->getMock(); + $container->expects($this->once())->method('get')->will($this->returnValue($renderer)); + + $handler = new LazyLoadingFragmentHandler($container, $requestStack, false); + $handler->addRendererService('foo', 'foo'); + + $handler->render('/foo', 'foo'); + + // second call should not lazy-load anymore (see once() above on the get() method) + $handler->render('/foo', 'foo'); + } + + public function testRender() + { + $renderer = $this->getMockBuilder('Symfony\Component\HttpKernel\Fragment\FragmentRendererInterface')->getMock(); + $renderer->expects($this->once())->method('getName')->will($this->returnValue('foo')); + $renderer->expects($this->any())->method('render')->will($this->returnValue(new Response())); + + $requestStack = $this->getMockBuilder('Symfony\Component\HttpFoundation\RequestStack')->getMock(); + $requestStack->expects($this->any())->method('getCurrentRequest')->will($this->returnValue(Request::create('/'))); + + $container = $this->getMockBuilder('Psr\Container\ContainerInterface')->getMock(); + $container->expects($this->once())->method('has')->with('foo')->willReturn(true); + $container->expects($this->once())->method('get')->will($this->returnValue($renderer)); + + $handler = new LazyLoadingFragmentHandler($container, $requestStack, false); + + $handler->render('/foo', 'foo'); + + // second call should not lazy-load anymore (see once() above on the get() method) + $handler->render('/foo', 'foo'); + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/Tests/DependencyInjection/LoggerPassTest.php b/lib/silex/vendor/symfony/http-kernel/Tests/DependencyInjection/LoggerPassTest.php new file mode 100644 index 000000000..b199e11da --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Tests/DependencyInjection/LoggerPassTest.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\HttpKernel\Tests\DependencyInjection; + +use PHPUnit\Framework\TestCase; +use Psr\Log\LoggerInterface; +use Symfony\Component\HttpKernel\DependencyInjection\LoggerPass; +use Symfony\Component\HttpKernel\Log\Logger; +use Symfony\Component\DependencyInjection\ContainerBuilder; + +/** + * @author Kévin Dunglas + */ +class LoggerPassTest extends TestCase +{ + public function testAlwaysSetAutowiringAlias() + { + $container = new ContainerBuilder(); + $container->register('logger', 'Foo'); + + (new LoggerPass())->process($container); + + $this->assertFalse($container->getAlias(LoggerInterface::class)->isPublic()); + } + + public function testDoNotOverrideExistingLogger() + { + $container = new ContainerBuilder(); + $container->register('logger', 'Foo'); + + (new LoggerPass())->process($container); + + $this->assertSame('Foo', $container->getDefinition('logger')->getClass()); + } + + public function testRegisterLogger() + { + $container = new ContainerBuilder(); + $container->setParameter('kernel.debug', false); + + (new LoggerPass())->process($container); + + $definition = $container->getDefinition('logger'); + $this->assertSame(Logger::class, $definition->getClass()); + $this->assertFalse($definition->isPublic()); + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/Tests/DependencyInjection/MergeExtensionConfigurationPassTest.php b/lib/silex/vendor/symfony/http-kernel/Tests/DependencyInjection/MergeExtensionConfigurationPassTest.php new file mode 100644 index 000000000..81fc8b455 --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Tests/DependencyInjection/MergeExtensionConfigurationPassTest.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\HttpKernel\Tests\DependencyInjection; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\HttpKernel\DependencyInjection\MergeExtensionConfigurationPass; + +class MergeExtensionConfigurationPassTest extends TestCase +{ + public function testAutoloadMainExtension() + { + $container = $this->getMockBuilder('Symfony\\Component\\DependencyInjection\\ContainerBuilder')->setMethods(array('getExtensionConfig', 'loadFromExtension', 'getParameterBag', 'getDefinitions', 'getAliases', 'getExtensions'))->getMock(); + $params = $this->getMockBuilder('Symfony\\Component\\DependencyInjection\\ParameterBag\\ParameterBag')->getMock(); + + $container->expects($this->at(0)) + ->method('getExtensionConfig') + ->with('loaded') + ->will($this->returnValue(array(array()))); + $container->expects($this->at(1)) + ->method('getExtensionConfig') + ->with('notloaded') + ->will($this->returnValue(array())); + $container->expects($this->once()) + ->method('loadFromExtension') + ->with('notloaded', array()); + + $container->expects($this->any()) + ->method('getParameterBag') + ->will($this->returnValue($params)); + $params->expects($this->any()) + ->method('all') + ->will($this->returnValue(array())); + $container->expects($this->any()) + ->method('getDefinitions') + ->will($this->returnValue(array())); + $container->expects($this->any()) + ->method('getAliases') + ->will($this->returnValue(array())); + $container->expects($this->any()) + ->method('getExtensions') + ->will($this->returnValue(array())); + + $configPass = new MergeExtensionConfigurationPass(array('loaded', 'notloaded')); + $configPass->process($container); + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/Tests/DependencyInjection/RegisterControllerArgumentLocatorsPassTest.php b/lib/silex/vendor/symfony/http-kernel/Tests/DependencyInjection/RegisterControllerArgumentLocatorsPassTest.php new file mode 100644 index 000000000..4016deb4a --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Tests/DependencyInjection/RegisterControllerArgumentLocatorsPassTest.php @@ -0,0 +1,389 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Tests\DependencyInjection; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument; +use Symfony\Component\DependencyInjection\ContainerAwareInterface; +use Symfony\Component\DependencyInjection\ContainerAwareTrait; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\DependencyInjection\ServiceLocator; +use Symfony\Component\DependencyInjection\TypedReference; +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\HttpKernel\DependencyInjection\RegisterControllerArgumentLocatorsPass; + +class RegisterControllerArgumentLocatorsPassTest extends TestCase +{ + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException + * @expectedExceptionMessage Class "Symfony\Component\HttpKernel\Tests\DependencyInjection\NotFound" used for service "foo" cannot be found. + */ + public function testInvalidClass() + { + $container = new ContainerBuilder(); + $container->register('argument_resolver.service')->addArgument(array()); + + $container->register('foo', NotFound::class) + ->addTag('controller.service_arguments') + ; + + $pass = new RegisterControllerArgumentLocatorsPass(); + $pass->process($container); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException + * @expectedExceptionMessage Missing "action" attribute on tag "controller.service_arguments" {"argument":"bar"} for service "foo". + */ + public function testNoAction() + { + $container = new ContainerBuilder(); + $container->register('argument_resolver.service')->addArgument(array()); + + $container->register('foo', RegisterTestController::class) + ->addTag('controller.service_arguments', array('argument' => 'bar')) + ; + + $pass = new RegisterControllerArgumentLocatorsPass(); + $pass->process($container); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException + * @expectedExceptionMessage Missing "argument" attribute on tag "controller.service_arguments" {"action":"fooAction"} for service "foo". + */ + public function testNoArgument() + { + $container = new ContainerBuilder(); + $container->register('argument_resolver.service')->addArgument(array()); + + $container->register('foo', RegisterTestController::class) + ->addTag('controller.service_arguments', array('action' => 'fooAction')) + ; + + $pass = new RegisterControllerArgumentLocatorsPass(); + $pass->process($container); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException + * @expectedExceptionMessage Missing "id" attribute on tag "controller.service_arguments" {"action":"fooAction","argument":"bar"} for service "foo". + */ + public function testNoService() + { + $container = new ContainerBuilder(); + $container->register('argument_resolver.service')->addArgument(array()); + + $container->register('foo', RegisterTestController::class) + ->addTag('controller.service_arguments', array('action' => 'fooAction', 'argument' => 'bar')) + ; + + $pass = new RegisterControllerArgumentLocatorsPass(); + $pass->process($container); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException + * @expectedExceptionMessage Invalid "action" attribute on tag "controller.service_arguments" for service "foo": no public "barAction()" method found on class "Symfony\Component\HttpKernel\Tests\DependencyInjection\RegisterTestController". + */ + public function testInvalidMethod() + { + $container = new ContainerBuilder(); + $container->register('argument_resolver.service')->addArgument(array()); + + $container->register('foo', RegisterTestController::class) + ->addTag('controller.service_arguments', array('action' => 'barAction', 'argument' => 'bar', 'id' => 'bar_service')) + ; + + $pass = new RegisterControllerArgumentLocatorsPass(); + $pass->process($container); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException + * @expectedExceptionMessage Invalid "controller.service_arguments" tag for service "foo": method "fooAction()" has no "baz" argument on class "Symfony\Component\HttpKernel\Tests\DependencyInjection\RegisterTestController". + */ + public function testInvalidArgument() + { + $container = new ContainerBuilder(); + $container->register('argument_resolver.service')->addArgument(array()); + + $container->register('foo', RegisterTestController::class) + ->addTag('controller.service_arguments', array('action' => 'fooAction', 'argument' => 'baz', 'id' => 'bar')) + ; + + $pass = new RegisterControllerArgumentLocatorsPass(); + $pass->process($container); + } + + public function testAllActions() + { + $container = new ContainerBuilder(); + $resolver = $container->register('argument_resolver.service')->addArgument(array()); + + $container->register('foo', RegisterTestController::class) + ->addTag('controller.service_arguments') + ; + + $pass = new RegisterControllerArgumentLocatorsPass(); + $pass->process($container); + + $locator = $container->getDefinition((string) $resolver->getArgument(0))->getArgument(0); + + $this->assertEquals(array('foo:fooAction'), array_keys($locator)); + $this->assertInstanceof(ServiceClosureArgument::class, $locator['foo:fooAction']); + + $locator = $container->getDefinition((string) $locator['foo:fooAction']->getValues()[0]); + + $this->assertSame(ServiceLocator::class, $locator->getClass()); + $this->assertFalse($locator->isPublic()); + + $expected = array('bar' => new ServiceClosureArgument(new TypedReference(ControllerDummy::class, ControllerDummy::class, RegisterTestController::class, ContainerInterface::IGNORE_ON_INVALID_REFERENCE))); + $this->assertEquals($expected, $locator->getArgument(0)); + } + + public function testExplicitArgument() + { + $container = new ContainerBuilder(); + $resolver = $container->register('argument_resolver.service')->addArgument(array()); + + $container->register('foo', RegisterTestController::class) + ->addTag('controller.service_arguments', array('action' => 'fooAction', 'argument' => 'bar', 'id' => 'bar')) + ->addTag('controller.service_arguments', array('action' => 'fooAction', 'argument' => 'bar', 'id' => 'baz')) // should be ignored, the first wins + ; + + $pass = new RegisterControllerArgumentLocatorsPass(); + $pass->process($container); + + $locator = $container->getDefinition((string) $resolver->getArgument(0))->getArgument(0); + $locator = $container->getDefinition((string) $locator['foo:fooAction']->getValues()[0]); + + $expected = array('bar' => new ServiceClosureArgument(new TypedReference('bar', ControllerDummy::class, RegisterTestController::class))); + $this->assertEquals($expected, $locator->getArgument(0)); + } + + public function testOptionalArgument() + { + $container = new ContainerBuilder(); + $resolver = $container->register('argument_resolver.service')->addArgument(array()); + + $container->register('foo', RegisterTestController::class) + ->addTag('controller.service_arguments', array('action' => 'fooAction', 'argument' => 'bar', 'id' => '?bar')) + ; + + $pass = new RegisterControllerArgumentLocatorsPass(); + $pass->process($container); + + $locator = $container->getDefinition((string) $resolver->getArgument(0))->getArgument(0); + $locator = $container->getDefinition((string) $locator['foo:fooAction']->getValues()[0]); + + $expected = array('bar' => new ServiceClosureArgument(new TypedReference('bar', ControllerDummy::class, RegisterTestController::class, ContainerInterface::IGNORE_ON_INVALID_REFERENCE))); + $this->assertEquals($expected, $locator->getArgument(0)); + } + + public function testSkipSetContainer() + { + $container = new ContainerBuilder(); + $resolver = $container->register('argument_resolver.service')->addArgument(array()); + + $container->register('foo', ContainerAwareRegisterTestController::class) + ->addTag('controller.service_arguments'); + + $pass = new RegisterControllerArgumentLocatorsPass(); + $pass->process($container); + + $locator = $container->getDefinition((string) $resolver->getArgument(0))->getArgument(0); + $this->assertSame(array('foo:fooAction'), array_keys($locator)); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException + * @expectedExceptionMessage Cannot determine controller argument for "Symfony\Component\HttpKernel\Tests\DependencyInjection\NonExistentClassController::fooAction()": the $nonExistent argument is type-hinted with the non-existent class or interface: "Symfony\Component\HttpKernel\Tests\DependencyInjection\NonExistentClass". Did you forget to add a use statement? + */ + public function testExceptionOnNonExistentTypeHint() + { + $container = new ContainerBuilder(); + $container->register('argument_resolver.service')->addArgument(array()); + + $container->register('foo', NonExistentClassController::class) + ->addTag('controller.service_arguments'); + + $pass = new RegisterControllerArgumentLocatorsPass(); + $pass->process($container); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException + * @expectedExceptionMessage Cannot determine controller argument for "Symfony\Component\HttpKernel\Tests\DependencyInjection\NonExistentClassDifferentNamespaceController::fooAction()": the $nonExistent argument is type-hinted with the non-existent class or interface: "Acme\NonExistentClass". + */ + public function testExceptionOnNonExistentTypeHintDifferentNamespace() + { + $container = new ContainerBuilder(); + $container->register('argument_resolver.service')->addArgument(array()); + + $container->register('foo', NonExistentClassDifferentNamespaceController::class) + ->addTag('controller.service_arguments'); + + $pass = new RegisterControllerArgumentLocatorsPass(); + $pass->process($container); + } + + public function testNoExceptionOnNonExistentTypeHintOptionalArg() + { + $container = new ContainerBuilder(); + $resolver = $container->register('argument_resolver.service')->addArgument(array()); + + $container->register('foo', NonExistentClassOptionalController::class) + ->addTag('controller.service_arguments'); + + $pass = new RegisterControllerArgumentLocatorsPass(); + $pass->process($container); + + $locator = $container->getDefinition((string) $resolver->getArgument(0))->getArgument(0); + $this->assertSame(array('foo:barAction', 'foo:fooAction'), array_keys($locator)); + } + + public function testArgumentWithNoTypeHintIsOk() + { + $container = new ContainerBuilder(); + $resolver = $container->register('argument_resolver.service')->addArgument(array()); + + $container->register('foo', ArgumentWithoutTypeController::class) + ->addTag('controller.service_arguments'); + + $pass = new RegisterControllerArgumentLocatorsPass(); + $pass->process($container); + + $locator = $container->getDefinition((string) $resolver->getArgument(0))->getArgument(0); + $this->assertEmpty(array_keys($locator)); + } + + public function testControllersAreMadePublic() + { + $container = new ContainerBuilder(); + $resolver = $container->register('argument_resolver.service')->addArgument(array()); + + $container->register('foo', ArgumentWithoutTypeController::class) + ->setPublic(false) + ->addTag('controller.service_arguments'); + + $pass = new RegisterControllerArgumentLocatorsPass(); + $pass->process($container); + + $this->assertTrue($container->getDefinition('foo')->isPublic()); + } + + /** + * @dataProvider provideBindings + */ + public function testBindings($bindingName) + { + $container = new ContainerBuilder(); + $resolver = $container->register('argument_resolver.service')->addArgument(array()); + + $container->register('foo', RegisterTestController::class) + ->setBindings(array($bindingName => new Reference('foo'))) + ->addTag('controller.service_arguments'); + + $pass = new RegisterControllerArgumentLocatorsPass(); + $pass->process($container); + + $locator = $container->getDefinition((string) $resolver->getArgument(0))->getArgument(0); + + $locator = $container->getDefinition((string) $locator['foo:fooAction']->getValues()[0]); + + $expected = array('bar' => new ServiceClosureArgument(new Reference('foo'))); + $this->assertEquals($expected, $locator->getArgument(0)); + } + + public function provideBindings() + { + return array(array(ControllerDummy::class), array('$bar')); + } + + public function testDoNotBindScalarValueToControllerArgument() + { + $container = new ContainerBuilder(); + $resolver = $container->register('argument_resolver.service')->addArgument(array()); + + $container->register('foo', ArgumentWithoutTypeController::class) + ->setBindings(array('$someArg' => '%foo%')) + ->addTag('controller.service_arguments'); + + $pass = new RegisterControllerArgumentLocatorsPass(); + $pass->process($container); + + $locator = $container->getDefinition((string) $resolver->getArgument(0))->getArgument(0); + $this->assertEmpty($locator); + } +} + +class RegisterTestController +{ + public function __construct(ControllerDummy $bar) + { + } + + public function fooAction(ControllerDummy $bar) + { + } + + protected function barAction(ControllerDummy $bar) + { + } +} + +class ContainerAwareRegisterTestController implements ContainerAwareInterface +{ + use ContainerAwareTrait; + + public function fooAction(ControllerDummy $bar) + { + } +} + +class ControllerDummy +{ +} + +class NonExistentClassController +{ + public function fooAction(NonExistentClass $nonExistent) + { + } +} + +class NonExistentClassDifferentNamespaceController +{ + public function fooAction(\Acme\NonExistentClass $nonExistent) + { + } +} + +class NonExistentClassOptionalController +{ + public function fooAction(NonExistentClass $nonExistent = null) + { + } + + public function barAction(NonExistentClass $nonExistent = null, $bar) + { + } +} + +class ArgumentWithoutTypeController +{ + public function fooAction($someArg) + { + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/Tests/DependencyInjection/RemoveEmptyControllerArgumentLocatorsPassTest.php b/lib/silex/vendor/symfony/http-kernel/Tests/DependencyInjection/RemoveEmptyControllerArgumentLocatorsPassTest.php new file mode 100644 index 000000000..9cac96818 --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Tests/DependencyInjection/RemoveEmptyControllerArgumentLocatorsPassTest.php @@ -0,0 +1,148 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Tests\DependencyInjection; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\DependencyInjection\Compiler\ResolveInvalidReferencesPass; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\HttpKernel\DependencyInjection\RegisterControllerArgumentLocatorsPass; +use Symfony\Component\HttpKernel\DependencyInjection\RemoveEmptyControllerArgumentLocatorsPass; + +class RemoveEmptyControllerArgumentLocatorsPassTest extends TestCase +{ + public function testProcess() + { + $container = new ContainerBuilder(); + $resolver = $container->register('argument_resolver.service')->addArgument(array()); + + $container->register('stdClass', 'stdClass'); + $container->register(parent::class, 'stdClass'); + $container->register('c1', RemoveTestController1::class)->addTag('controller.service_arguments'); + $container->register('c2', RemoveTestController2::class)->addTag('controller.service_arguments') + ->addMethodCall('setTestCase', array(new Reference('c1'))); + + $pass = new RegisterControllerArgumentLocatorsPass(); + $pass->process($container); + + $controllers = $container->getDefinition((string) $resolver->getArgument(0))->getArgument(0); + + $this->assertCount(2, $container->getDefinition((string) $controllers['c1:fooAction']->getValues()[0])->getArgument(0)); + $this->assertCount(1, $container->getDefinition((string) $controllers['c2:setTestCase']->getValues()[0])->getArgument(0)); + $this->assertCount(1, $container->getDefinition((string) $controllers['c2:fooAction']->getValues()[0])->getArgument(0)); + + (new ResolveInvalidReferencesPass())->process($container); + + $this->assertCount(1, $container->getDefinition((string) $controllers['c2:setTestCase']->getValues()[0])->getArgument(0)); + $this->assertSame(array(), $container->getDefinition((string) $controllers['c2:fooAction']->getValues()[0])->getArgument(0)); + + (new RemoveEmptyControllerArgumentLocatorsPass())->process($container); + + $controllers = $container->getDefinition((string) $resolver->getArgument(0))->getArgument(0); + + $this->assertSame(array('c1:fooAction'), array_keys($controllers)); + $this->assertSame(array('bar'), array_keys($container->getDefinition((string) $controllers['c1:fooAction']->getValues()[0])->getArgument(0))); + + $expectedLog = array( + 'Symfony\Component\HttpKernel\DependencyInjection\RemoveEmptyControllerArgumentLocatorsPass: Removing service-argument resolver for controller "c2:fooAction": no corresponding services exist for the referenced types.', + 'Symfony\Component\HttpKernel\DependencyInjection\RemoveEmptyControllerArgumentLocatorsPass: Removing method "setTestCase" of service "c2" from controller candidates: the method is called at instantiation, thus cannot be an action.', + ); + + $this->assertSame($expectedLog, $container->getCompiler()->getLog()); + } + + public function testSameIdClass() + { + $container = new ContainerBuilder(); + $resolver = $container->register('argument_resolver.service')->addArgument(array()); + + $container->register(RegisterTestController::class, RegisterTestController::class) + ->addTag('controller.service_arguments') + ; + + (new RegisterControllerArgumentLocatorsPass())->process($container); + (new RemoveEmptyControllerArgumentLocatorsPass())->process($container); + + $expected = array( + RegisterTestController::class.':fooAction', + RegisterTestController::class.'::fooAction', + ); + $this->assertEquals($expected, array_keys($container->getDefinition((string) $resolver->getArgument(0))->getArgument(0))); + } + + public function testInvoke() + { + $container = new ContainerBuilder(); + $resolver = $container->register('argument_resolver.service')->addArgument(array()); + + $container->register('invokable', InvokableRegisterTestController::class) + ->addTag('controller.service_arguments') + ; + + (new RegisterControllerArgumentLocatorsPass())->process($container); + (new RemoveEmptyControllerArgumentLocatorsPass())->process($container); + + $this->assertEquals( + array('invokable:__invoke', 'invokable'), + array_keys($container->getDefinition((string) $resolver->getArgument(0))->getArgument(0)) + ); + } + + public function testInvokeSameIdClass() + { + $container = new ContainerBuilder(); + $resolver = $container->register('argument_resolver.service')->addArgument(array()); + + $container->register(InvokableRegisterTestController::class, InvokableRegisterTestController::class) + ->addTag('controller.service_arguments') + ; + + (new RegisterControllerArgumentLocatorsPass())->process($container); + (new RemoveEmptyControllerArgumentLocatorsPass())->process($container); + + $expected = array( + InvokableRegisterTestController::class.':__invoke', + InvokableRegisterTestController::class.'::__invoke', + InvokableRegisterTestController::class, + ); + $this->assertEquals($expected, array_keys($container->getDefinition((string) $resolver->getArgument(0))->getArgument(0))); + } +} + +class RemoveTestController1 +{ + public function fooAction(\stdClass $bar, ClassNotInContainer $baz) + { + } +} + +class RemoveTestController2 +{ + public function setTestCase(TestCase $test) + { + } + + public function fooAction(ClassNotInContainer $bar) + { + } +} + +class InvokableRegisterTestController +{ + public function __invoke(\stdClass $bar) + { + } +} + +class ClassNotInContainer +{ +} diff --git a/lib/silex/vendor/symfony/http-kernel/Tests/DependencyInjection/ResettableServicePassTest.php b/lib/silex/vendor/symfony/http-kernel/Tests/DependencyInjection/ResettableServicePassTest.php new file mode 100644 index 000000000..f7ea16dbf --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Tests/DependencyInjection/ResettableServicePassTest.php @@ -0,0 +1,78 @@ +register('one', ResettableService::class) + ->setPublic(true) + ->addTag('kernel.reset', array('method' => 'reset')); + $container->register('two', ClearableService::class) + ->setPublic(true) + ->addTag('kernel.reset', array('method' => 'clear')); + + $container->register('services_resetter', ServicesResetter::class) + ->setPublic(true) + ->setArguments(array(null, array())); + $container->addCompilerPass(new ResettableServicePass()); + + $container->compile(); + + $definition = $container->getDefinition('services_resetter'); + + $this->assertEquals( + array( + new IteratorArgument(array( + 'one' => new Reference('one', ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE), + 'two' => new Reference('two', ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE), + )), + array( + 'one' => 'reset', + 'two' => 'clear', + ), + ), + $definition->getArguments() + ); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException + * @expectedExceptionMessage Tag kernel.reset requires the "method" attribute to be set. + */ + public function testMissingMethod() + { + $container = new ContainerBuilder(); + $container->register(ResettableService::class) + ->addTag('kernel.reset'); + $container->register('services_resetter', ServicesResetter::class) + ->setArguments(array(null, array())); + $container->addCompilerPass(new ResettableServicePass()); + + $container->compile(); + } + + public function testCompilerPassWithoutResetters() + { + $container = new ContainerBuilder(); + $container->register('services_resetter', ServicesResetter::class) + ->setArguments(array(null, array())); + $container->addCompilerPass(new ResettableServicePass()); + + $container->compile(); + + $this->assertFalse($container->has('services_resetter')); + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/Tests/DependencyInjection/ServicesResetterTest.php b/lib/silex/vendor/symfony/http-kernel/Tests/DependencyInjection/ServicesResetterTest.php new file mode 100644 index 000000000..47c62abd0 --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Tests/DependencyInjection/ServicesResetterTest.php @@ -0,0 +1,42 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Tests\DependencyInjection; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\HttpKernel\DependencyInjection\ServicesResetter; +use Symfony\Component\HttpKernel\Tests\Fixtures\ClearableService; +use Symfony\Component\HttpKernel\Tests\Fixtures\ResettableService; + +class ServicesResetterTest extends TestCase +{ + protected function setUp() + { + ResettableService::$counter = 0; + ClearableService::$counter = 0; + } + + public function testResetServices() + { + $resetter = new ServicesResetter(new \ArrayIterator(array( + 'id1' => new ResettableService(), + 'id2' => new ClearableService(), + )), array( + 'id1' => 'reset', + 'id2' => 'clear', + )); + + $resetter->reset(); + + $this->assertEquals(1, ResettableService::$counter); + $this->assertEquals(1, ClearableService::$counter); + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/Tests/Event/FilterControllerArgumentsEventTest.php b/lib/silex/vendor/symfony/http-kernel/Tests/Event/FilterControllerArgumentsEventTest.php new file mode 100644 index 000000000..9165d31f2 --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Tests/Event/FilterControllerArgumentsEventTest.php @@ -0,0 +1,17 @@ +assertEquals($filterController->getArguments(), array('test')); + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/Tests/Event/GetResponseForExceptionEventTest.php b/lib/silex/vendor/symfony/http-kernel/Tests/Event/GetResponseForExceptionEventTest.php new file mode 100644 index 000000000..724257930 --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Tests/Event/GetResponseForExceptionEventTest.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Tests\Event; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent; +use Symfony\Component\HttpKernel\Tests\TestHttpKernel; + +class GetResponseForExceptionEventTest extends TestCase +{ + public function testAllowSuccessfulResponseIsFalseByDefault() + { + $event = new GetResponseForExceptionEvent(new TestHttpKernel(), new Request(), 1, new \Exception()); + + $this->assertFalse($event->isAllowingCustomResponseCode()); + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/Tests/EventListener/AddRequestFormatsListenerTest.php b/lib/silex/vendor/symfony/http-kernel/Tests/EventListener/AddRequestFormatsListenerTest.php new file mode 100644 index 000000000..f4878f626 --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Tests/EventListener/AddRequestFormatsListenerTest.php @@ -0,0 +1,84 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Tests\EventListener; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\HttpKernel\EventListener\AddRequestFormatsListener; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpKernel\KernelEvents; + +/** + * Test AddRequestFormatsListener class. + * + * @author Gildas Quemener + */ +class AddRequestFormatsListenerTest extends TestCase +{ + /** + * @var AddRequestFormatsListener + */ + private $listener; + + protected function setUp() + { + $this->listener = new AddRequestFormatsListener(array('csv' => array('text/csv', 'text/plain'))); + } + + protected function tearDown() + { + $this->listener = null; + } + + public function testIsAnEventSubscriber() + { + $this->assertInstanceOf('Symfony\Component\EventDispatcher\EventSubscriberInterface', $this->listener); + } + + public function testRegisteredEvent() + { + $this->assertEquals( + array(KernelEvents::REQUEST => array('onKernelRequest', 1)), + AddRequestFormatsListener::getSubscribedEvents() + ); + } + + public function testSetAdditionalFormats() + { + $request = $this->getRequestMock(); + $event = $this->getGetResponseEventMock($request); + + $request->expects($this->once()) + ->method('setFormat') + ->with('csv', array('text/csv', 'text/plain')); + + $this->listener->onKernelRequest($event); + } + + protected function getRequestMock() + { + return $this->getMockBuilder('Symfony\Component\HttpFoundation\Request')->getMock(); + } + + protected function getGetResponseEventMock(Request $request) + { + $event = $this + ->getMockBuilder('Symfony\Component\HttpKernel\Event\GetResponseEvent') + ->disableOriginalConstructor() + ->getMock(); + + $event->expects($this->any()) + ->method('getRequest') + ->will($this->returnValue($request)); + + return $event; + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/Tests/EventListener/DebugHandlersListenerTest.php b/lib/silex/vendor/symfony/http-kernel/Tests/EventListener/DebugHandlersListenerTest.php new file mode 100644 index 000000000..467b8dde8 --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Tests/EventListener/DebugHandlersListenerTest.php @@ -0,0 +1,155 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Tests\EventListener; + +use PHPUnit\Framework\TestCase; +use Psr\Log\LogLevel; +use Symfony\Component\Console\Event\ConsoleEvent; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\ConsoleEvents; +use Symfony\Component\Console\Helper\HelperSet; +use Symfony\Component\Console\Input\ArgvInput; +use Symfony\Component\Console\Output\ConsoleOutput; +use Symfony\Component\Debug\ErrorHandler; +use Symfony\Component\Debug\ExceptionHandler; +use Symfony\Component\EventDispatcher\EventDispatcher; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpKernel\Event\KernelEvent; +use Symfony\Component\HttpKernel\EventListener\DebugHandlersListener; +use Symfony\Component\HttpKernel\HttpKernelInterface; +use Symfony\Component\HttpKernel\KernelEvents; + +/** + * @author Nicolas Grekas + */ +class DebugHandlersListenerTest extends TestCase +{ + public function testConfigure() + { + $logger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock(); + $userHandler = function () {}; + $listener = new DebugHandlersListener($userHandler, $logger); + $xHandler = new ExceptionHandler(); + $eHandler = new ErrorHandler(); + $eHandler->setExceptionHandler(array($xHandler, 'handle')); + + $exception = null; + set_error_handler(array($eHandler, 'handleError')); + set_exception_handler(array($eHandler, 'handleException')); + try { + $listener->configure(); + } catch (\Exception $exception) { + } + restore_exception_handler(); + restore_error_handler(); + + if (null !== $exception) { + throw $exception; + } + + $this->assertSame($userHandler, $xHandler->setHandler('var_dump')); + + $loggers = $eHandler->setLoggers(array()); + + $this->assertArrayHasKey(E_DEPRECATED, $loggers); + $this->assertSame(array($logger, LogLevel::INFO), $loggers[E_DEPRECATED]); + } + + public function testConfigureForHttpKernelWithNoTerminateWithException() + { + $listener = new DebugHandlersListener(null); + $eHandler = new ErrorHandler(); + $event = new KernelEvent( + $this->getMockBuilder('Symfony\Component\HttpKernel\HttpKernelInterface')->getMock(), + Request::create('/'), + HttpKernelInterface::MASTER_REQUEST + ); + + $exception = null; + $h = set_exception_handler(array($eHandler, 'handleException')); + try { + $listener->configure($event); + } catch (\Exception $exception) { + } + restore_exception_handler(); + + if (null !== $exception) { + throw $exception; + } + + $this->assertNull($h); + } + + public function testConsoleEvent() + { + $dispatcher = new EventDispatcher(); + $listener = new DebugHandlersListener(null); + $app = $this->getMockBuilder('Symfony\Component\Console\Application')->getMock(); + $app->expects($this->once())->method('getHelperSet')->will($this->returnValue(new HelperSet())); + $command = new Command(__FUNCTION__); + $command->setApplication($app); + $event = new ConsoleEvent($command, new ArgvInput(), new ConsoleOutput()); + + $dispatcher->addSubscriber($listener); + + $xListeners = array( + KernelEvents::REQUEST => array(array($listener, 'configure')), + ConsoleEvents::COMMAND => array(array($listener, 'configure')), + ); + $this->assertSame($xListeners, $dispatcher->getListeners()); + + $exception = null; + $eHandler = new ErrorHandler(); + set_error_handler(array($eHandler, 'handleError')); + set_exception_handler(array($eHandler, 'handleException')); + try { + $dispatcher->dispatch(ConsoleEvents::COMMAND, $event); + } catch (\Exception $exception) { + } + restore_exception_handler(); + restore_error_handler(); + + if (null !== $exception) { + throw $exception; + } + + $xHandler = $eHandler->setExceptionHandler('var_dump'); + $this->assertInstanceOf('Closure', $xHandler); + + $app->expects($this->once()) + ->method('renderException'); + + $xHandler(new \Exception()); + } + + public function testReplaceExistingExceptionHandler() + { + $userHandler = function () {}; + $listener = new DebugHandlersListener($userHandler); + $eHandler = new ErrorHandler(); + $eHandler->setExceptionHandler('var_dump'); + + $exception = null; + set_exception_handler(array($eHandler, 'handleException')); + try { + $listener->configure(); + } catch (\Exception $exception) { + } + restore_exception_handler(); + + if (null !== $exception) { + throw $exception; + } + + $this->assertSame($userHandler, $eHandler->setExceptionHandler('var_dump')); + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/Tests/EventListener/DumpListenerTest.php b/lib/silex/vendor/symfony/http-kernel/Tests/EventListener/DumpListenerTest.php new file mode 100644 index 000000000..509f44308 --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Tests/EventListener/DumpListenerTest.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\HttpKernel\Tests\EventListener; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Console\ConsoleEvents; +use Symfony\Component\HttpKernel\EventListener\DumpListener; +use Symfony\Component\VarDumper\Cloner\ClonerInterface; +use Symfony\Component\VarDumper\Cloner\Data; +use Symfony\Component\VarDumper\Dumper\DataDumperInterface; +use Symfony\Component\VarDumper\VarDumper; + +/** + * DumpListenerTest. + * + * @author Nicolas Grekas + */ +class DumpListenerTest extends TestCase +{ + public function testSubscribedEvents() + { + $this->assertSame( + array(ConsoleEvents::COMMAND => array('configure', 1024)), + DumpListener::getSubscribedEvents() + ); + } + + public function testConfigure() + { + $prevDumper = VarDumper::setHandler('var_dump'); + VarDumper::setHandler($prevDumper); + + $cloner = new MockCloner(); + $dumper = new MockDumper(); + + ob_start(); + $exception = null; + $listener = new DumpListener($cloner, $dumper); + + try { + $listener->configure(); + + VarDumper::dump('foo'); + VarDumper::dump('bar'); + + $this->assertSame('+foo-+bar-', ob_get_clean()); + } catch (\Exception $exception) { + } + + VarDumper::setHandler($prevDumper); + + if (null !== $exception) { + throw $exception; + } + } +} + +class MockCloner implements ClonerInterface +{ + public function cloneVar($var) + { + return new Data(array(array($var.'-'))); + } +} + +class MockDumper implements DataDumperInterface +{ + public function dump(Data $data) + { + echo '+'.$data->getValue(); + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/Tests/EventListener/ExceptionListenerTest.php b/lib/silex/vendor/symfony/http-kernel/Tests/EventListener/ExceptionListenerTest.php new file mode 100644 index 000000000..3cb0b298b --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Tests/EventListener/ExceptionListenerTest.php @@ -0,0 +1,178 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Tests\EventListener; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\EventDispatcher\EventDispatcher; +use Symfony\Component\HttpKernel\Event\FilterResponseEvent; +use Symfony\Component\HttpKernel\HttpKernelInterface; +use Symfony\Component\HttpKernel\EventListener\ExceptionListener; +use Symfony\Component\HttpKernel\KernelEvents; +use Symfony\Component\HttpKernel\Log\DebugLoggerInterface; +use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\Tests\Logger; + +/** + * ExceptionListenerTest. + * + * @author Robert Schönthal + * + * @group time-sensitive + */ +class ExceptionListenerTest extends TestCase +{ + public function testConstruct() + { + $logger = new TestLogger(); + $l = new ExceptionListener('foo', $logger); + + $_logger = new \ReflectionProperty(get_class($l), 'logger'); + $_logger->setAccessible(true); + $_controller = new \ReflectionProperty(get_class($l), 'controller'); + $_controller->setAccessible(true); + + $this->assertSame($logger, $_logger->getValue($l)); + $this->assertSame('foo', $_controller->getValue($l)); + } + + /** + * @dataProvider provider + */ + public function testHandleWithoutLogger($event, $event2) + { + $this->iniSet('error_log', file_exists('/dev/null') ? '/dev/null' : 'nul'); + + $l = new ExceptionListener('foo'); + $l->onKernelException($event); + + $this->assertEquals(new Response('foo'), $event->getResponse()); + + try { + $l->onKernelException($event2); + $this->fail('RuntimeException expected'); + } catch (\RuntimeException $e) { + $this->assertSame('bar', $e->getMessage()); + $this->assertSame('foo', $e->getPrevious()->getMessage()); + } + } + + /** + * @dataProvider provider + */ + public function testHandleWithLogger($event, $event2) + { + $logger = new TestLogger(); + + $l = new ExceptionListener('foo', $logger); + $l->onKernelException($event); + + $this->assertEquals(new Response('foo'), $event->getResponse()); + + try { + $l->onKernelException($event2); + $this->fail('RuntimeException expected'); + } catch (\RuntimeException $e) { + $this->assertSame('bar', $e->getMessage()); + $this->assertSame('foo', $e->getPrevious()->getMessage()); + } + + $this->assertEquals(3, $logger->countErrors()); + $this->assertCount(3, $logger->getLogs('critical')); + } + + public function provider() + { + if (!class_exists('Symfony\Component\HttpFoundation\Request')) { + return array(array(null, null)); + } + + $request = new Request(); + $exception = new \Exception('foo'); + $event = new GetResponseForExceptionEvent(new TestKernel(), $request, HttpKernelInterface::MASTER_REQUEST, $exception); + $event2 = new GetResponseForExceptionEvent(new TestKernelThatThrowsException(), $request, HttpKernelInterface::MASTER_REQUEST, $exception); + + return array( + array($event, $event2), + ); + } + + public function testSubRequestFormat() + { + $listener = new ExceptionListener('foo', $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock()); + + $kernel = $this->getMockBuilder('Symfony\Component\HttpKernel\HttpKernelInterface')->getMock(); + $kernel->expects($this->once())->method('handle')->will($this->returnCallback(function (Request $request) { + return new Response($request->getRequestFormat()); + })); + + $request = Request::create('/'); + $request->setRequestFormat('xml'); + + $event = new GetResponseForExceptionEvent($kernel, $request, HttpKernelInterface::MASTER_REQUEST, new \Exception('foo')); + $listener->onKernelException($event); + + $response = $event->getResponse(); + $this->assertEquals('xml', $response->getContent()); + } + + public function testCSPHeaderIsRemoved() + { + $dispatcher = new EventDispatcher(); + $kernel = $this->getMockBuilder('Symfony\Component\HttpKernel\HttpKernelInterface')->getMock(); + $kernel->expects($this->once())->method('handle')->will($this->returnCallback(function (Request $request) { + return new Response($request->getRequestFormat()); + })); + + $listener = new ExceptionListener('foo', $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock(), true); + + $dispatcher->addSubscriber($listener); + + $request = Request::create('/'); + $event = new GetResponseForExceptionEvent($kernel, $request, HttpKernelInterface::MASTER_REQUEST, new \Exception('foo')); + $dispatcher->dispatch(KernelEvents::EXCEPTION, $event); + + $response = new Response('', 200, array('content-security-policy' => "style-src 'self'")); + $this->assertTrue($response->headers->has('content-security-policy')); + + $event = new FilterResponseEvent($kernel, $request, HttpKernelInterface::MASTER_REQUEST, $response); + $dispatcher->dispatch(KernelEvents::RESPONSE, $event); + + $this->assertFalse($response->headers->has('content-security-policy'), 'CSP header has been removed'); + $this->assertFalse($dispatcher->hasListeners(KernelEvents::RESPONSE), 'CSP removal listener has been removed'); + } +} + +class TestLogger extends Logger implements DebugLoggerInterface +{ + public function countErrors() + { + return count($this->logs['critical']); + } +} + +class TestKernel implements HttpKernelInterface +{ + public function handle(Request $request, $type = self::MASTER_REQUEST, $catch = true) + { + return new Response('foo'); + } +} + +class TestKernelThatThrowsException implements HttpKernelInterface +{ + public function handle(Request $request, $type = self::MASTER_REQUEST, $catch = true) + { + throw new \RuntimeException('bar'); + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/Tests/EventListener/FragmentListenerTest.php b/lib/silex/vendor/symfony/http-kernel/Tests/EventListener/FragmentListenerTest.php new file mode 100644 index 000000000..464b2ab46 --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Tests/EventListener/FragmentListenerTest.php @@ -0,0 +1,122 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Tests\EventListener; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\HttpKernel\EventListener\FragmentListener; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpKernel\HttpKernelInterface; +use Symfony\Component\HttpKernel\Event\GetResponseEvent; +use Symfony\Component\HttpKernel\UriSigner; + +class FragmentListenerTest extends TestCase +{ + public function testOnlyTriggeredOnFragmentRoute() + { + $request = Request::create('http://example.com/foo?_path=foo%3Dbar%26_controller%3Dfoo'); + + $listener = new FragmentListener(new UriSigner('foo')); + $event = $this->createGetResponseEvent($request); + + $expected = $request->attributes->all(); + + $listener->onKernelRequest($event); + + $this->assertEquals($expected, $request->attributes->all()); + $this->assertTrue($request->query->has('_path')); + } + + public function testOnlyTriggeredIfControllerWasNotDefinedYet() + { + $request = Request::create('http://example.com/_fragment?_path=foo%3Dbar%26_controller%3Dfoo'); + $request->attributes->set('_controller', 'bar'); + + $listener = new FragmentListener(new UriSigner('foo')); + $event = $this->createGetResponseEvent($request, HttpKernelInterface::SUB_REQUEST); + + $expected = $request->attributes->all(); + + $listener->onKernelRequest($event); + + $this->assertEquals($expected, $request->attributes->all()); + } + + /** + * @expectedException \Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException + */ + public function testAccessDeniedWithNonSafeMethods() + { + $request = Request::create('http://example.com/_fragment', 'POST'); + + $listener = new FragmentListener(new UriSigner('foo')); + $event = $this->createGetResponseEvent($request); + + $listener->onKernelRequest($event); + } + + /** + * @expectedException \Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException + */ + public function testAccessDeniedWithWrongSignature() + { + $request = Request::create('http://example.com/_fragment', 'GET', array(), array(), array(), array('REMOTE_ADDR' => '10.0.0.1')); + + $listener = new FragmentListener(new UriSigner('foo')); + $event = $this->createGetResponseEvent($request); + + $listener->onKernelRequest($event); + } + + public function testWithSignature() + { + $signer = new UriSigner('foo'); + $request = Request::create($signer->sign('http://example.com/_fragment?_path=foo%3Dbar%26_controller%3Dfoo'), 'GET', array(), array(), array(), array('REMOTE_ADDR' => '10.0.0.1')); + + $listener = new FragmentListener($signer); + $event = $this->createGetResponseEvent($request); + + $listener->onKernelRequest($event); + + $this->assertEquals(array('foo' => 'bar', '_controller' => 'foo'), $request->attributes->get('_route_params')); + $this->assertFalse($request->query->has('_path')); + } + + public function testRemovesPathWithControllerDefined() + { + $request = Request::create('http://example.com/_fragment?_path=foo%3Dbar%26_controller%3Dfoo'); + + $listener = new FragmentListener(new UriSigner('foo')); + $event = $this->createGetResponseEvent($request, HttpKernelInterface::SUB_REQUEST); + + $listener->onKernelRequest($event); + + $this->assertFalse($request->query->has('_path')); + } + + public function testRemovesPathWithControllerNotDefined() + { + $signer = new UriSigner('foo'); + $request = Request::create($signer->sign('http://example.com/_fragment?_path=foo%3Dbar'), 'GET', array(), array(), array(), array('REMOTE_ADDR' => '10.0.0.1')); + + $listener = new FragmentListener($signer); + $event = $this->createGetResponseEvent($request); + + $listener->onKernelRequest($event); + + $this->assertFalse($request->query->has('_path')); + } + + private function createGetResponseEvent(Request $request, $requestType = HttpKernelInterface::MASTER_REQUEST) + { + return new GetResponseEvent($this->getMockBuilder('Symfony\Component\HttpKernel\HttpKernelInterface')->getMock(), $request, $requestType); + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/Tests/EventListener/LocaleListenerTest.php b/lib/silex/vendor/symfony/http-kernel/Tests/EventListener/LocaleListenerTest.php new file mode 100644 index 000000000..332393eea --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Tests/EventListener/LocaleListenerTest.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\HttpKernel\Tests\EventListener; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpKernel\EventListener\LocaleListener; +use Symfony\Component\HttpKernel\HttpKernelInterface; +use Symfony\Component\HttpKernel\Event\GetResponseEvent; + +class LocaleListenerTest extends TestCase +{ + private $requestStack; + + protected function setUp() + { + $this->requestStack = $this->getMockBuilder('Symfony\Component\HttpFoundation\RequestStack')->disableOriginalConstructor()->getMock(); + } + + public function testDefaultLocaleWithoutSession() + { + $listener = new LocaleListener($this->requestStack, 'fr'); + $event = $this->getEvent($request = Request::create('/')); + + $listener->onKernelRequest($event); + $this->assertEquals('fr', $request->getLocale()); + } + + public function testLocaleFromRequestAttribute() + { + $request = Request::create('/'); + $request->cookies->set(session_name(), 'value'); + + $request->attributes->set('_locale', 'es'); + $listener = new LocaleListener($this->requestStack, 'fr'); + $event = $this->getEvent($request); + + $listener->onKernelRequest($event); + $this->assertEquals('es', $request->getLocale()); + } + + public function testLocaleSetForRoutingContext() + { + // the request context is updated + $context = $this->getMockBuilder('Symfony\Component\Routing\RequestContext')->getMock(); + $context->expects($this->once())->method('setParameter')->with('_locale', 'es'); + + $router = $this->getMockBuilder('Symfony\Component\Routing\Router')->setMethods(array('getContext'))->disableOriginalConstructor()->getMock(); + $router->expects($this->once())->method('getContext')->will($this->returnValue($context)); + + $request = Request::create('/'); + + $request->attributes->set('_locale', 'es'); + $listener = new LocaleListener($this->requestStack, 'fr', $router); + $listener->onKernelRequest($this->getEvent($request)); + } + + public function testRouterResetWithParentRequestOnKernelFinishRequest() + { + // the request context is updated + $context = $this->getMockBuilder('Symfony\Component\Routing\RequestContext')->getMock(); + $context->expects($this->once())->method('setParameter')->with('_locale', 'es'); + + $router = $this->getMockBuilder('Symfony\Component\Routing\Router')->setMethods(array('getContext'))->disableOriginalConstructor()->getMock(); + $router->expects($this->once())->method('getContext')->will($this->returnValue($context)); + + $parentRequest = Request::create('/'); + $parentRequest->setLocale('es'); + + $this->requestStack->expects($this->once())->method('getParentRequest')->will($this->returnValue($parentRequest)); + + $event = $this->getMockBuilder('Symfony\Component\HttpKernel\Event\FinishRequestEvent')->disableOriginalConstructor()->getMock(); + + $listener = new LocaleListener($this->requestStack, 'fr', $router); + $listener->onKernelFinishRequest($event); + } + + public function testRequestLocaleIsNotOverridden() + { + $request = Request::create('/'); + $request->setLocale('de'); + $listener = new LocaleListener($this->requestStack, 'fr'); + $event = $this->getEvent($request); + + $listener->onKernelRequest($event); + $this->assertEquals('de', $request->getLocale()); + } + + private function getEvent(Request $request) + { + return new GetResponseEvent($this->getMockBuilder('Symfony\Component\HttpKernel\HttpKernelInterface')->getMock(), $request, HttpKernelInterface::MASTER_REQUEST); + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/Tests/EventListener/ProfilerListenerTest.php b/lib/silex/vendor/symfony/http-kernel/Tests/EventListener/ProfilerListenerTest.php new file mode 100644 index 000000000..751aee869 --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Tests/EventListener/ProfilerListenerTest.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\HttpKernel\Tests\EventListener; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\HttpFoundation\RequestStack; +use Symfony\Component\HttpKernel\EventListener\ProfilerListener; +use Symfony\Component\HttpKernel\Event\FilterResponseEvent; +use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent; +use Symfony\Component\HttpKernel\Event\PostResponseEvent; +use Symfony\Component\HttpKernel\Exception\HttpException; +use Symfony\Component\HttpKernel\Kernel; + +class ProfilerListenerTest extends TestCase +{ + /** + * Test a master and sub request with an exception and `onlyException` profiler option enabled. + */ + public function testKernelTerminate() + { + $profile = $this->getMockBuilder('Symfony\Component\HttpKernel\Profiler\Profile') + ->disableOriginalConstructor() + ->getMock(); + + $profiler = $this->getMockBuilder('Symfony\Component\HttpKernel\Profiler\Profiler') + ->disableOriginalConstructor() + ->getMock(); + + $profiler->expects($this->once()) + ->method('collect') + ->will($this->returnValue($profile)); + + $kernel = $this->getMockBuilder('Symfony\Component\HttpKernel\HttpKernelInterface')->getMock(); + + $masterRequest = $this->getMockBuilder('Symfony\Component\HttpFoundation\Request') + ->disableOriginalConstructor() + ->getMock(); + + $subRequest = $this->getMockBuilder('Symfony\Component\HttpFoundation\Request') + ->disableOriginalConstructor() + ->getMock(); + + $response = $this->getMockBuilder('Symfony\Component\HttpFoundation\Response') + ->disableOriginalConstructor() + ->getMock(); + + $requestStack = new RequestStack(); + $requestStack->push($masterRequest); + + $onlyException = true; + $listener = new ProfilerListener($profiler, $requestStack, null, $onlyException); + + // master request + $listener->onKernelResponse(new FilterResponseEvent($kernel, $masterRequest, Kernel::MASTER_REQUEST, $response)); + + // sub request + $listener->onKernelException(new GetResponseForExceptionEvent($kernel, $subRequest, Kernel::SUB_REQUEST, new HttpException(404))); + $listener->onKernelResponse(new FilterResponseEvent($kernel, $subRequest, Kernel::SUB_REQUEST, $response)); + + $listener->onKernelTerminate(new PostResponseEvent($kernel, $masterRequest, $response)); + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/Tests/EventListener/ResponseListenerTest.php b/lib/silex/vendor/symfony/http-kernel/Tests/EventListener/ResponseListenerTest.php new file mode 100644 index 000000000..12a31eb3e --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Tests/EventListener/ResponseListenerTest.php @@ -0,0 +1,95 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Tests\EventListener; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\HttpKernel\EventListener\ResponseListener; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\HttpKernelInterface; +use Symfony\Component\HttpKernel\Event\FilterResponseEvent; +use Symfony\Component\HttpKernel\KernelEvents; +use Symfony\Component\EventDispatcher\EventDispatcher; + +class ResponseListenerTest extends TestCase +{ + private $dispatcher; + + private $kernel; + + protected function setUp() + { + $this->dispatcher = new EventDispatcher(); + $listener = new ResponseListener('UTF-8'); + $this->dispatcher->addListener(KernelEvents::RESPONSE, array($listener, 'onKernelResponse')); + + $this->kernel = $this->getMockBuilder('Symfony\Component\HttpKernel\HttpKernelInterface')->getMock(); + } + + protected function tearDown() + { + $this->dispatcher = null; + $this->kernel = null; + } + + public function testFilterDoesNothingForSubRequests() + { + $response = new Response('foo'); + + $event = new FilterResponseEvent($this->kernel, new Request(), HttpKernelInterface::SUB_REQUEST, $response); + $this->dispatcher->dispatch(KernelEvents::RESPONSE, $event); + + $this->assertEquals('', $event->getResponse()->headers->get('content-type')); + } + + public function testFilterSetsNonDefaultCharsetIfNotOverridden() + { + $listener = new ResponseListener('ISO-8859-15'); + $this->dispatcher->addListener(KernelEvents::RESPONSE, array($listener, 'onKernelResponse'), 1); + + $response = new Response('foo'); + + $event = new FilterResponseEvent($this->kernel, Request::create('/'), HttpKernelInterface::MASTER_REQUEST, $response); + $this->dispatcher->dispatch(KernelEvents::RESPONSE, $event); + + $this->assertEquals('ISO-8859-15', $response->getCharset()); + } + + public function testFilterDoesNothingIfCharsetIsOverridden() + { + $listener = new ResponseListener('ISO-8859-15'); + $this->dispatcher->addListener(KernelEvents::RESPONSE, array($listener, 'onKernelResponse'), 1); + + $response = new Response('foo'); + $response->setCharset('ISO-8859-1'); + + $event = new FilterResponseEvent($this->kernel, Request::create('/'), HttpKernelInterface::MASTER_REQUEST, $response); + $this->dispatcher->dispatch(KernelEvents::RESPONSE, $event); + + $this->assertEquals('ISO-8859-1', $response->getCharset()); + } + + public function testFiltersSetsNonDefaultCharsetIfNotOverriddenOnNonTextContentType() + { + $listener = new ResponseListener('ISO-8859-15'); + $this->dispatcher->addListener(KernelEvents::RESPONSE, array($listener, 'onKernelResponse'), 1); + + $response = new Response('foo'); + $request = Request::create('/'); + $request->setRequestFormat('application/json'); + + $event = new FilterResponseEvent($this->kernel, $request, HttpKernelInterface::MASTER_REQUEST, $response); + $this->dispatcher->dispatch(KernelEvents::RESPONSE, $event); + + $this->assertEquals('ISO-8859-15', $response->getCharset()); + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/Tests/EventListener/RouterListenerTest.php b/lib/silex/vendor/symfony/http-kernel/Tests/EventListener/RouterListenerTest.php new file mode 100644 index 000000000..342dfc367 --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Tests/EventListener/RouterListenerTest.php @@ -0,0 +1,226 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Tests\EventListener; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\EventDispatcher\EventDispatcher; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\RequestStack; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\Controller\ArgumentResolver; +use Symfony\Component\HttpKernel\Controller\ControllerResolver; +use Symfony\Component\HttpKernel\EventListener\ExceptionListener; +use Symfony\Component\HttpKernel\EventListener\RouterListener; +use Symfony\Component\HttpKernel\EventListener\ValidateRequestListener; +use Symfony\Component\HttpKernel\HttpKernelInterface; +use Symfony\Component\HttpKernel\HttpKernel; +use Symfony\Component\HttpKernel\Event\GetResponseEvent; +use Symfony\Component\Routing\Exception\NoConfigurationException; +use Symfony\Component\Routing\RequestContext; + +class RouterListenerTest extends TestCase +{ + private $requestStack; + + protected function setUp() + { + $this->requestStack = $this->getMockBuilder('Symfony\Component\HttpFoundation\RequestStack')->disableOriginalConstructor()->getMock(); + } + + /** + * @dataProvider getPortData + */ + public function testPort($defaultHttpPort, $defaultHttpsPort, $uri, $expectedHttpPort, $expectedHttpsPort) + { + $urlMatcher = $this->getMockBuilder('Symfony\Component\Routing\Matcher\UrlMatcherInterface') + ->disableOriginalConstructor() + ->getMock(); + $context = new RequestContext(); + $context->setHttpPort($defaultHttpPort); + $context->setHttpsPort($defaultHttpsPort); + $urlMatcher->expects($this->any()) + ->method('getContext') + ->will($this->returnValue($context)); + + $listener = new RouterListener($urlMatcher, $this->requestStack); + $event = $this->createGetResponseEventForUri($uri); + $listener->onKernelRequest($event); + + $this->assertEquals($expectedHttpPort, $context->getHttpPort()); + $this->assertEquals($expectedHttpsPort, $context->getHttpsPort()); + $this->assertEquals(0 === strpos($uri, 'https') ? 'https' : 'http', $context->getScheme()); + } + + public function getPortData() + { + return array( + array(80, 443, 'http://localhost/', 80, 443), + array(80, 443, 'http://localhost:90/', 90, 443), + array(80, 443, 'https://localhost/', 80, 443), + array(80, 443, 'https://localhost:90/', 80, 90), + ); + } + + /** + * @param string $uri + * + * @return GetResponseEvent + */ + private function createGetResponseEventForUri($uri) + { + $kernel = $this->getMockBuilder('Symfony\Component\HttpKernel\HttpKernelInterface')->getMock(); + $request = Request::create($uri); + $request->attributes->set('_controller', null); // Prevents going in to routing process + + return new GetResponseEvent($kernel, $request, HttpKernelInterface::MASTER_REQUEST); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testInvalidMatcher() + { + new RouterListener(new \stdClass(), $this->requestStack); + } + + public function testRequestMatcher() + { + $kernel = $this->getMockBuilder('Symfony\Component\HttpKernel\HttpKernelInterface')->getMock(); + $request = Request::create('http://localhost/'); + $event = new GetResponseEvent($kernel, $request, HttpKernelInterface::MASTER_REQUEST); + + $requestMatcher = $this->getMockBuilder('Symfony\Component\Routing\Matcher\RequestMatcherInterface')->getMock(); + $requestMatcher->expects($this->once()) + ->method('matchRequest') + ->with($this->isInstanceOf('Symfony\Component\HttpFoundation\Request')) + ->will($this->returnValue(array())); + + $listener = new RouterListener($requestMatcher, $this->requestStack, new RequestContext()); + $listener->onKernelRequest($event); + } + + public function testSubRequestWithDifferentMethod() + { + $kernel = $this->getMockBuilder('Symfony\Component\HttpKernel\HttpKernelInterface')->getMock(); + $request = Request::create('http://localhost/', 'post'); + $event = new GetResponseEvent($kernel, $request, HttpKernelInterface::MASTER_REQUEST); + + $requestMatcher = $this->getMockBuilder('Symfony\Component\Routing\Matcher\RequestMatcherInterface')->getMock(); + $requestMatcher->expects($this->any()) + ->method('matchRequest') + ->with($this->isInstanceOf('Symfony\Component\HttpFoundation\Request')) + ->will($this->returnValue(array())); + + $context = new RequestContext(); + + $listener = new RouterListener($requestMatcher, $this->requestStack, new RequestContext()); + $listener->onKernelRequest($event); + + // sub-request with another HTTP method + $kernel = $this->getMockBuilder('Symfony\Component\HttpKernel\HttpKernelInterface')->getMock(); + $request = Request::create('http://localhost/', 'get'); + $event = new GetResponseEvent($kernel, $request, HttpKernelInterface::SUB_REQUEST); + + $listener->onKernelRequest($event); + + $this->assertEquals('GET', $context->getMethod()); + } + + /** + * @dataProvider getLoggingParameterData + */ + public function testLoggingParameter($parameter, $log, $parameters) + { + $requestMatcher = $this->getMockBuilder('Symfony\Component\Routing\Matcher\RequestMatcherInterface')->getMock(); + $requestMatcher->expects($this->once()) + ->method('matchRequest') + ->will($this->returnValue($parameter)); + + $logger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock(); + $logger->expects($this->once()) + ->method('info') + ->with($this->equalTo($log), $this->equalTo($parameters)); + + $kernel = $this->getMockBuilder('Symfony\Component\HttpKernel\HttpKernelInterface')->getMock(); + $request = Request::create('http://localhost/'); + + $listener = new RouterListener($requestMatcher, $this->requestStack, new RequestContext(), $logger); + $listener->onKernelRequest(new GetResponseEvent($kernel, $request, HttpKernelInterface::MASTER_REQUEST)); + } + + public function getLoggingParameterData() + { + return array( + array(array('_route' => 'foo'), 'Matched route "{route}".', array('route' => 'foo', 'route_parameters' => array('_route' => 'foo'), 'request_uri' => 'http://localhost/', 'method' => 'GET')), + array(array(), 'Matched route "{route}".', array('route' => 'n/a', 'route_parameters' => array(), 'request_uri' => 'http://localhost/', 'method' => 'GET')), + ); + } + + public function testWithBadRequest() + { + $requestStack = new RequestStack(); + + $requestMatcher = $this->getMockBuilder('Symfony\Component\Routing\Matcher\RequestMatcherInterface')->getMock(); + $requestMatcher->expects($this->never())->method('matchRequest'); + + $dispatcher = new EventDispatcher(); + $dispatcher->addSubscriber(new ValidateRequestListener()); + $dispatcher->addSubscriber(new RouterListener($requestMatcher, $requestStack, new RequestContext())); + $dispatcher->addSubscriber(new ExceptionListener(function () { + return new Response('Exception handled', 400); + })); + + $kernel = new HttpKernel($dispatcher, new ControllerResolver(), $requestStack, new ArgumentResolver()); + + $request = Request::create('http://localhost/'); + $request->headers->set('host', '###'); + $response = $kernel->handle($request); + $this->assertSame(400, $response->getStatusCode()); + } + + public function testNoRoutingConfigurationResponse() + { + $requestStack = new RequestStack(); + + $requestMatcher = $this->getMockBuilder('Symfony\Component\Routing\Matcher\RequestMatcherInterface')->getMock(); + $requestMatcher + ->expects($this->once()) + ->method('matchRequest') + ->willThrowException(new NoConfigurationException()) + ; + + $dispatcher = new EventDispatcher(); + $dispatcher->addSubscriber(new RouterListener($requestMatcher, $requestStack, new RequestContext())); + + $kernel = new HttpKernel($dispatcher, new ControllerResolver(), $requestStack, new ArgumentResolver()); + + $request = Request::create('http://localhost/'); + $response = $kernel->handle($request); + $this->assertSame(404, $response->getStatusCode()); + $this->assertContains('Welcome', $response->getContent()); + } + + /** + * @expectedException \Symfony\Component\HttpKernel\Exception\BadRequestHttpException + */ + public function testRequestWithBadHost() + { + $kernel = $this->getMockBuilder('Symfony\Component\HttpKernel\HttpKernelInterface')->getMock(); + $request = Request::create('http://bad host %22/'); + $event = new GetResponseEvent($kernel, $request, HttpKernelInterface::MASTER_REQUEST); + + $requestMatcher = $this->getMockBuilder('Symfony\Component\Routing\Matcher\RequestMatcherInterface')->getMock(); + + $listener = new RouterListener($requestMatcher, $this->requestStack, new RequestContext()); + $listener->onKernelRequest($event); + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/Tests/EventListener/SaveSessionListenerTest.php b/lib/silex/vendor/symfony/http-kernel/Tests/EventListener/SaveSessionListenerTest.php new file mode 100644 index 000000000..5492c3d78 --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Tests/EventListener/SaveSessionListenerTest.php @@ -0,0 +1,49 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Tests\EventListener; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpFoundation\Session\SessionInterface; +use Symfony\Component\HttpKernel\Event\FilterResponseEvent; +use Symfony\Component\HttpKernel\EventListener\SaveSessionListener; +use Symfony\Component\HttpKernel\HttpKernelInterface; + +class SaveSessionListenerTest extends TestCase +{ + public function testOnlyTriggeredOnMasterRequest() + { + $listener = new SaveSessionListener(); + $event = $this->getMockBuilder(FilterResponseEvent::class)->disableOriginalConstructor()->getMock(); + $event->expects($this->once())->method('isMasterRequest')->willReturn(false); + $event->expects($this->never())->method('getRequest'); + + // sub request + $listener->onKernelResponse($event); + } + + public function testSessionSaved() + { + $listener = new SaveSessionListener(); + $kernel = $this->getMockBuilder(HttpKernelInterface::class)->disableOriginalConstructor()->getMock(); + + $session = $this->getMockBuilder(SessionInterface::class)->disableOriginalConstructor()->getMock(); + $session->expects($this->once())->method('isStarted')->willReturn(true); + $session->expects($this->once())->method('save'); + + $request = new Request(); + $request->setSession($session); + $response = new Response(); + $listener->onKernelResponse(new FilterResponseEvent($kernel, $request, HttpKernelInterface::MASTER_REQUEST, $response)); + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/Tests/EventListener/SessionListenerTest.php b/lib/silex/vendor/symfony/http-kernel/Tests/EventListener/SessionListenerTest.php new file mode 100644 index 000000000..34598363c --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Tests/EventListener/SessionListenerTest.php @@ -0,0 +1,79 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Tests\EventListener; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\DependencyInjection\Container; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpFoundation\Session\Session; +use Symfony\Component\HttpKernel\Event\GetResponseEvent; +use Symfony\Component\HttpKernel\Event\FilterResponseEvent; +use Symfony\Component\HttpKernel\EventListener\AbstractSessionListener; +use Symfony\Component\HttpKernel\EventListener\SessionListener; +use Symfony\Component\HttpKernel\HttpKernelInterface; + +class SessionListenerTest extends TestCase +{ + public function testOnlyTriggeredOnMasterRequest() + { + $listener = $this->getMockForAbstractClass(AbstractSessionListener::class); + $event = $this->getMockBuilder(GetResponseEvent::class)->disableOriginalConstructor()->getMock(); + $event->expects($this->once())->method('isMasterRequest')->willReturn(false); + $event->expects($this->never())->method('getRequest'); + + // sub request + $listener->onKernelRequest($event); + } + + public function testSessionIsSet() + { + $session = $this->getMockBuilder(Session::class)->disableOriginalConstructor()->getMock(); + + $container = new Container(); + $container->set('session', $session); + + $request = new Request(); + $listener = new SessionListener($container); + + $event = $this->getMockBuilder(GetResponseEvent::class)->disableOriginalConstructor()->getMock(); + $event->expects($this->once())->method('isMasterRequest')->willReturn(true); + $event->expects($this->once())->method('getRequest')->willReturn($request); + + $listener->onKernelRequest($event); + + $this->assertTrue($request->hasSession()); + $this->assertSame($session, $request->getSession()); + } + + public function testResponseIsPrivate() + { + $session = $this->getMockBuilder(Session::class)->disableOriginalConstructor()->getMock(); + $session->expects($this->once())->method('isStarted')->willReturn(false); + $session->expects($this->once())->method('hasBeenStarted')->willReturn(true); + + $container = new Container(); + $container->set('session', $session); + + $listener = new SessionListener($container); + $kernel = $this->getMockBuilder(HttpKernelInterface::class)->disableOriginalConstructor()->getMock(); + + $request = new Request(); + $response = new Response(); + $listener->onKernelRequest(new GetResponseEvent($kernel, $request, HttpKernelInterface::MASTER_REQUEST)); + $listener->onKernelResponse(new FilterResponseEvent($kernel, $request, HttpKernelInterface::MASTER_REQUEST, $response)); + + $this->assertTrue($response->headers->hasCacheControlDirective('private')); + $this->assertTrue($response->headers->hasCacheControlDirective('must-revalidate')); + $this->assertSame('0', $response->headers->getCacheControlDirective('max-age')); + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/Tests/EventListener/SurrogateListenerTest.php b/lib/silex/vendor/symfony/http-kernel/Tests/EventListener/SurrogateListenerTest.php new file mode 100644 index 000000000..1e79ebe05 --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Tests/EventListener/SurrogateListenerTest.php @@ -0,0 +1,67 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Tests\EventListener; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\HttpKernel\HttpCache\Esi; +use Symfony\Component\HttpKernel\EventListener\SurrogateListener; +use Symfony\Component\HttpKernel\Event\FilterResponseEvent; +use Symfony\Component\HttpKernel\KernelEvents; +use Symfony\Component\HttpKernel\HttpKernelInterface; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\EventDispatcher\EventDispatcher; + +class SurrogateListenerTest extends TestCase +{ + public function testFilterDoesNothingForSubRequests() + { + $dispatcher = new EventDispatcher(); + $kernel = $this->getMockBuilder('Symfony\Component\HttpKernel\HttpKernelInterface')->getMock(); + $response = new Response('foo '); + $listener = new SurrogateListener(new Esi()); + + $dispatcher->addListener(KernelEvents::RESPONSE, array($listener, 'onKernelResponse')); + $event = new FilterResponseEvent($kernel, new Request(), HttpKernelInterface::SUB_REQUEST, $response); + $dispatcher->dispatch(KernelEvents::RESPONSE, $event); + + $this->assertEquals('', $event->getResponse()->headers->get('Surrogate-Control')); + } + + public function testFilterWhenThereIsSomeEsiIncludes() + { + $dispatcher = new EventDispatcher(); + $kernel = $this->getMockBuilder('Symfony\Component\HttpKernel\HttpKernelInterface')->getMock(); + $response = new Response('foo '); + $listener = new SurrogateListener(new Esi()); + + $dispatcher->addListener(KernelEvents::RESPONSE, array($listener, 'onKernelResponse')); + $event = new FilterResponseEvent($kernel, new Request(), HttpKernelInterface::MASTER_REQUEST, $response); + $dispatcher->dispatch(KernelEvents::RESPONSE, $event); + + $this->assertEquals('content="ESI/1.0"', $event->getResponse()->headers->get('Surrogate-Control')); + } + + public function testFilterWhenThereIsNoEsiIncludes() + { + $dispatcher = new EventDispatcher(); + $kernel = $this->getMockBuilder('Symfony\Component\HttpKernel\HttpKernelInterface')->getMock(); + $response = new Response('foo'); + $listener = new SurrogateListener(new Esi()); + + $dispatcher->addListener(KernelEvents::RESPONSE, array($listener, 'onKernelResponse')); + $event = new FilterResponseEvent($kernel, new Request(), HttpKernelInterface::MASTER_REQUEST, $response); + $dispatcher->dispatch(KernelEvents::RESPONSE, $event); + + $this->assertEquals('', $event->getResponse()->headers->get('Surrogate-Control')); + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/Tests/EventListener/TestSessionListenerTest.php b/lib/silex/vendor/symfony/http-kernel/Tests/EventListener/TestSessionListenerTest.php new file mode 100644 index 000000000..4452f4877 --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Tests/EventListener/TestSessionListenerTest.php @@ -0,0 +1,164 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Tests\EventListener; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\DependencyInjection\ServiceSubscriberInterface; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpKernel\HttpKernelInterface; +use Symfony\Component\HttpKernel\Event\FilterResponseEvent; +use Symfony\Component\HttpKernel\EventListener\SessionListener; +use Symfony\Component\HttpKernel\EventListener\TestSessionListener; +use Symfony\Component\HttpFoundation\Session\SessionInterface; + +/** + * SessionListenerTest. + * + * Tests SessionListener. + * + * @author Bulat Shakirzyanov + */ +class TestSessionListenerTest extends TestCase +{ + /** + * @var TestSessionListener + */ + private $listener; + + /** + * @var SessionInterface + */ + private $session; + + protected function setUp() + { + $this->listener = $this->getMockForAbstractClass('Symfony\Component\HttpKernel\EventListener\AbstractTestSessionListener'); + $this->session = $this->getSession(); + } + + public function testShouldSaveMasterRequestSession() + { + $this->sessionHasBeenStarted(); + $this->sessionMustBeSaved(); + + $this->filterResponse(new Request()); + } + + public function testShouldNotSaveSubRequestSession() + { + $this->sessionMustNotBeSaved(); + + $this->filterResponse(new Request(), HttpKernelInterface::SUB_REQUEST); + } + + public function testDoesNotDeleteCookieIfUsingSessionLifetime() + { + $this->sessionHasBeenStarted(); + + @ini_set('session.cookie_lifetime', 0); + + $response = $this->filterResponse(new Request(), HttpKernelInterface::MASTER_REQUEST); + $cookies = $response->headers->getCookies(); + + $this->assertEquals(0, reset($cookies)->getExpiresTime()); + } + + /** + * @requires function \Symfony\Component\HttpFoundation\Session\Session::isEmpty + */ + public function testEmptySessionDoesNotSendCookie() + { + $this->sessionHasBeenStarted(); + $this->sessionIsEmpty(); + + $response = $this->filterResponse(new Request(), HttpKernelInterface::MASTER_REQUEST); + + $this->assertSame(array(), $response->headers->getCookies()); + } + + public function testUnstartedSessionIsNotSave() + { + $this->sessionHasNotBeenStarted(); + $this->sessionMustNotBeSaved(); + + $this->filterResponse(new Request()); + } + + public function testDoesNotImplementServiceSubscriberInterface() + { + $this->assertTrue(interface_exists(ServiceSubscriberInterface::class)); + $this->assertTrue(class_exists(SessionListener::class)); + $this->assertTrue(class_exists(TestSessionListener::class)); + $this->assertFalse(is_subclass_of(SessionListener::class, ServiceSubscriberInterface::class), 'Implementing ServiceSubscriberInterface would create a dep on the DI component, which eg Silex cannot afford'); + $this->assertFalse(is_subclass_of(TestSessionListener::class, ServiceSubscriberInterface::class, 'Implementing ServiceSubscriberInterface would create a dep on the DI component, which eg Silex cannot afford')); + } + + private function filterResponse(Request $request, $type = HttpKernelInterface::MASTER_REQUEST) + { + $request->setSession($this->session); + $response = new Response(); + $kernel = $this->getMockBuilder('Symfony\Component\HttpKernel\HttpKernelInterface')->getMock(); + $event = new FilterResponseEvent($kernel, $request, $type, $response); + + $this->listener->onKernelResponse($event); + + $this->assertSame($response, $event->getResponse()); + + return $response; + } + + private function sessionMustNotBeSaved() + { + $this->session->expects($this->never()) + ->method('save'); + } + + private function sessionMustBeSaved() + { + $this->session->expects($this->once()) + ->method('save'); + } + + private function sessionHasBeenStarted() + { + $this->session->expects($this->once()) + ->method('isStarted') + ->will($this->returnValue(true)); + } + + private function sessionHasNotBeenStarted() + { + $this->session->expects($this->once()) + ->method('isStarted') + ->will($this->returnValue(false)); + } + + private function sessionIsEmpty() + { + $this->session->expects($this->once()) + ->method('isEmpty') + ->will($this->returnValue(true)); + } + + private function getSession() + { + $mock = $this->getMockBuilder('Symfony\Component\HttpFoundation\Session\Session') + ->disableOriginalConstructor() + ->getMock(); + + // set return value for getName() + $mock->expects($this->any())->method('getName')->will($this->returnValue('MOCKSESSID')); + + return $mock; + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/Tests/EventListener/TranslatorListenerTest.php b/lib/silex/vendor/symfony/http-kernel/Tests/EventListener/TranslatorListenerTest.php new file mode 100644 index 000000000..23b833177 --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Tests/EventListener/TranslatorListenerTest.php @@ -0,0 +1,118 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Tests\EventListener; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpKernel\Event\FinishRequestEvent; +use Symfony\Component\HttpKernel\Event\GetResponseEvent; +use Symfony\Component\HttpKernel\EventListener\TranslatorListener; +use Symfony\Component\HttpKernel\HttpKernelInterface; + +class TranslatorListenerTest extends TestCase +{ + private $listener; + private $translator; + private $requestStack; + + protected function setUp() + { + $this->translator = $this->getMockBuilder('Symfony\Component\Translation\TranslatorInterface')->getMock(); + $this->requestStack = $this->getMockBuilder('Symfony\Component\HttpFoundation\RequestStack')->getMock(); + $this->listener = new TranslatorListener($this->translator, $this->requestStack); + } + + public function testLocaleIsSetInOnKernelRequest() + { + $this->translator + ->expects($this->once()) + ->method('setLocale') + ->with($this->equalTo('fr')); + + $event = new GetResponseEvent($this->createHttpKernel(), $this->createRequest('fr'), HttpKernelInterface::MASTER_REQUEST); + $this->listener->onKernelRequest($event); + } + + public function testDefaultLocaleIsUsedOnExceptionsInOnKernelRequest() + { + $this->translator + ->expects($this->at(0)) + ->method('setLocale') + ->will($this->throwException(new \InvalidArgumentException())); + $this->translator + ->expects($this->at(1)) + ->method('setLocale') + ->with($this->equalTo('en')); + + $event = new GetResponseEvent($this->createHttpKernel(), $this->createRequest('fr'), HttpKernelInterface::MASTER_REQUEST); + $this->listener->onKernelRequest($event); + } + + public function testLocaleIsSetInOnKernelFinishRequestWhenParentRequestExists() + { + $this->translator + ->expects($this->once()) + ->method('setLocale') + ->with($this->equalTo('fr')); + + $this->setMasterRequest($this->createRequest('fr')); + $event = new FinishRequestEvent($this->createHttpKernel(), $this->createRequest('de'), HttpKernelInterface::SUB_REQUEST); + $this->listener->onKernelFinishRequest($event); + } + + public function testLocaleIsNotSetInOnKernelFinishRequestWhenParentRequestDoesNotExist() + { + $this->translator + ->expects($this->never()) + ->method('setLocale'); + + $event = new FinishRequestEvent($this->createHttpKernel(), $this->createRequest('de'), HttpKernelInterface::SUB_REQUEST); + $this->listener->onKernelFinishRequest($event); + } + + public function testDefaultLocaleIsUsedOnExceptionsInOnKernelFinishRequest() + { + $this->translator + ->expects($this->at(0)) + ->method('setLocale') + ->will($this->throwException(new \InvalidArgumentException())); + $this->translator + ->expects($this->at(1)) + ->method('setLocale') + ->with($this->equalTo('en')); + + $this->setMasterRequest($this->createRequest('fr')); + $event = new FinishRequestEvent($this->createHttpKernel(), $this->createRequest('de'), HttpKernelInterface::SUB_REQUEST); + $this->listener->onKernelFinishRequest($event); + } + + private function createHttpKernel() + { + return $this->getMockBuilder('Symfony\Component\HttpKernel\HttpKernelInterface')->getMock(); + } + + private function createRequest($locale) + { + $request = new Request(); + $request->setLocale($locale); + + return $request; + } + + private function setMasterRequest($request) + { + $this->requestStack + ->expects($this->any()) + ->method('getParentRequest') + ->will($this->returnValue($request)); + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/Tests/EventListener/ValidateRequestListenerTest.php b/lib/silex/vendor/symfony/http-kernel/Tests/EventListener/ValidateRequestListenerTest.php new file mode 100644 index 000000000..d06094325 --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Tests/EventListener/ValidateRequestListenerTest.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\HttpKernel\Tests\EventListener; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\EventDispatcher\EventDispatcher; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpKernel\EventListener\ValidateRequestListener; +use Symfony\Component\HttpKernel\Event\GetResponseEvent; +use Symfony\Component\HttpKernel\HttpKernelInterface; +use Symfony\Component\HttpKernel\KernelEvents; + +class ValidateRequestListenerTest extends TestCase +{ + /** + * @expectedException \Symfony\Component\HttpFoundation\Exception\ConflictingHeadersException + */ + public function testListenerThrowsWhenMasterRequestHasInconsistentClientIps() + { + $dispatcher = new EventDispatcher(); + $kernel = $this->getMockBuilder('Symfony\Component\HttpKernel\HttpKernelInterface')->getMock(); + + $request = new Request(); + $request->setTrustedProxies(array('1.1.1.1'), Request::HEADER_X_FORWARDED_FOR | Request::HEADER_FORWARDED); + $request->server->set('REMOTE_ADDR', '1.1.1.1'); + $request->headers->set('FORWARDED', 'for=2.2.2.2'); + $request->headers->set('X_FORWARDED_FOR', '3.3.3.3'); + + $dispatcher->addListener(KernelEvents::REQUEST, array(new ValidateRequestListener(), 'onKernelRequest')); + $event = new GetResponseEvent($kernel, $request, HttpKernelInterface::MASTER_REQUEST); + + $dispatcher->dispatch(KernelEvents::REQUEST, $event); + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/Tests/Exception/AccessDeniedHttpExceptionTest.php b/lib/silex/vendor/symfony/http-kernel/Tests/Exception/AccessDeniedHttpExceptionTest.php new file mode 100644 index 000000000..2bfcb2bf8 --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Tests/Exception/AccessDeniedHttpExceptionTest.php @@ -0,0 +1,13 @@ + 'Test')), + array(array('X-Test' => 1)), + array( + array( + array('X-Test' => 'Test'), + array('X-Test-2' => 'Test-2'), + ), + ), + ); + } + + public function testHeadersDefault() + { + $exception = $this->createException(); + $this->assertSame(array(), $exception->getHeaders()); + } + + /** + * @dataProvider headerDataProvider + */ + public function testHeadersConstructor($headers) + { + $exception = new HttpException(200, null, null, $headers); + $this->assertSame($headers, $exception->getHeaders()); + } + + /** + * @dataProvider headerDataProvider + */ + public function testHeadersSetter($headers) + { + $exception = $this->createException(); + $exception->setHeaders($headers); + $this->assertSame($headers, $exception->getHeaders()); + } + + protected function createException() + { + return new HttpException(200); + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/Tests/Exception/LengthRequiredHttpExceptionTest.php b/lib/silex/vendor/symfony/http-kernel/Tests/Exception/LengthRequiredHttpExceptionTest.php new file mode 100644 index 000000000..462d3ca4f --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Tests/Exception/LengthRequiredHttpExceptionTest.php @@ -0,0 +1,13 @@ +assertSame(array('Allow' => 'GET, PUT'), $exception->getHeaders()); + } + + /** + * @dataProvider headerDataProvider + */ + public function testHeadersSetter($headers) + { + $exception = new MethodNotAllowedHttpException(array('GET')); + $exception->setHeaders($headers); + $this->assertSame($headers, $exception->getHeaders()); + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/Tests/Exception/NotAcceptableHttpExceptionTest.php b/lib/silex/vendor/symfony/http-kernel/Tests/Exception/NotAcceptableHttpExceptionTest.php new file mode 100644 index 000000000..4c0db7a3c --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Tests/Exception/NotAcceptableHttpExceptionTest.php @@ -0,0 +1,13 @@ +assertSame(array('Retry-After' => 10), $exception->getHeaders()); + } + + /** + * @dataProvider headerDataProvider + */ + public function testHeadersSetter($headers) + { + $exception = new ServiceUnavailableHttpException(10); + $exception->setHeaders($headers); + $this->assertSame($headers, $exception->getHeaders()); + } + + protected function createException() + { + return new ServiceUnavailableHttpException(); + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/Tests/Exception/TooManyRequestsHttpExceptionTest.php b/lib/silex/vendor/symfony/http-kernel/Tests/Exception/TooManyRequestsHttpExceptionTest.php new file mode 100644 index 000000000..2079bb338 --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Tests/Exception/TooManyRequestsHttpExceptionTest.php @@ -0,0 +1,29 @@ +assertSame(array('Retry-After' => 10), $exception->getHeaders()); + } + + /** + * @dataProvider headerDataProvider + */ + public function testHeadersSetter($headers) + { + $exception = new TooManyRequestsHttpException(10); + $exception->setHeaders($headers); + $this->assertSame($headers, $exception->getHeaders()); + } + + protected function createException() + { + return new TooManyRequestsHttpException(); + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/Tests/Exception/UnauthorizedHttpExceptionTest.php b/lib/silex/vendor/symfony/http-kernel/Tests/Exception/UnauthorizedHttpExceptionTest.php new file mode 100644 index 000000000..37a0028dc --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Tests/Exception/UnauthorizedHttpExceptionTest.php @@ -0,0 +1,24 @@ +assertSame(array('WWW-Authenticate' => 'Challenge'), $exception->getHeaders()); + } + + /** + * @dataProvider headerDataProvider + */ + public function testHeadersSetter($headers) + { + $exception = new UnauthorizedHttpException('Challenge'); + $exception->setHeaders($headers); + $this->assertSame($headers, $exception->getHeaders()); + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/Tests/Exception/UnprocessableEntityHttpExceptionTest.php b/lib/silex/vendor/symfony/http-kernel/Tests/Exception/UnprocessableEntityHttpExceptionTest.php new file mode 100644 index 000000000..760366c69 --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Tests/Exception/UnprocessableEntityHttpExceptionTest.php @@ -0,0 +1,28 @@ +setHeaders($headers); + $this->assertSame($headers, $exception->getHeaders()); + } + + protected function createException() + { + return new UnprocessableEntityHttpException(); + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/Tests/Exception/UnsupportedMediaTypeHttpExceptionTest.php b/lib/silex/vendor/symfony/http-kernel/Tests/Exception/UnsupportedMediaTypeHttpExceptionTest.php new file mode 100644 index 000000000..d47287a1f --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Tests/Exception/UnsupportedMediaTypeHttpExceptionTest.php @@ -0,0 +1,23 @@ +setHeaders($headers); + $this->assertSame($headers, $exception->getHeaders()); + } + + protected function createException($headers = array()) + { + return new UnsupportedMediaTypeHttpException(); + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/Tests/Fixtures/123/Kernel123.php b/lib/silex/vendor/symfony/http-kernel/Tests/Fixtures/123/Kernel123.php new file mode 100644 index 000000000..b6cf1cba2 --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Tests/Fixtures/123/Kernel123.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\HttpKernel\Tests\Fixtures\_123; + +use Symfony\Component\HttpKernel\Kernel; +use Symfony\Component\Config\Loader\LoaderInterface; + +class Kernel123 extends Kernel +{ + public function registerBundles() + { + return array(); + } + + public function registerContainerConfiguration(LoaderInterface $loader) + { + } + + public function getCacheDir() + { + return sys_get_temp_dir().'/'.Kernel::VERSION.'/kernel123/cache/'.$this->environment; + } + + public function getLogDir() + { + return sys_get_temp_dir().'/'.Kernel::VERSION.'/kernel123/logs'; + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/Tests/Fixtures/BaseBundle/Resources/foo.txt b/lib/silex/vendor/symfony/http-kernel/Tests/Fixtures/BaseBundle/Resources/foo.txt new file mode 100644 index 000000000..e69de29bb diff --git a/lib/silex/vendor/symfony/http-kernel/Tests/Fixtures/BaseBundle/Resources/hide.txt b/lib/silex/vendor/symfony/http-kernel/Tests/Fixtures/BaseBundle/Resources/hide.txt new file mode 100644 index 000000000..e69de29bb diff --git a/lib/silex/vendor/symfony/http-kernel/Tests/Fixtures/Bundle1Bundle/Resources/foo.txt b/lib/silex/vendor/symfony/http-kernel/Tests/Fixtures/Bundle1Bundle/Resources/foo.txt new file mode 100644 index 000000000..e69de29bb diff --git a/lib/silex/vendor/symfony/http-kernel/Tests/Fixtures/Bundle1Bundle/bar.txt b/lib/silex/vendor/symfony/http-kernel/Tests/Fixtures/Bundle1Bundle/bar.txt new file mode 100644 index 000000000..e69de29bb diff --git a/lib/silex/vendor/symfony/http-kernel/Tests/Fixtures/Bundle1Bundle/foo.txt b/lib/silex/vendor/symfony/http-kernel/Tests/Fixtures/Bundle1Bundle/foo.txt new file mode 100644 index 000000000..e69de29bb diff --git a/lib/silex/vendor/symfony/http-kernel/Tests/Fixtures/Bundle2Bundle/foo.txt b/lib/silex/vendor/symfony/http-kernel/Tests/Fixtures/Bundle2Bundle/foo.txt new file mode 100644 index 000000000..e69de29bb diff --git a/lib/silex/vendor/symfony/http-kernel/Tests/Fixtures/ChildBundle/Resources/foo.txt b/lib/silex/vendor/symfony/http-kernel/Tests/Fixtures/ChildBundle/Resources/foo.txt new file mode 100644 index 000000000..e69de29bb diff --git a/lib/silex/vendor/symfony/http-kernel/Tests/Fixtures/ChildBundle/Resources/hide.txt b/lib/silex/vendor/symfony/http-kernel/Tests/Fixtures/ChildBundle/Resources/hide.txt new file mode 100644 index 000000000..e69de29bb diff --git a/lib/silex/vendor/symfony/http-kernel/Tests/Fixtures/ClearableService.php b/lib/silex/vendor/symfony/http-kernel/Tests/Fixtures/ClearableService.php new file mode 100644 index 000000000..35acb419c --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Tests/Fixtures/ClearableService.php @@ -0,0 +1,13 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Tests\Fixtures\Controller; + +class BasicTypesController +{ + public function action(string $foo, int $bar, float $baz) + { + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/Tests/Fixtures/Controller/ExtendingRequest.php b/lib/silex/vendor/symfony/http-kernel/Tests/Fixtures/Controller/ExtendingRequest.php new file mode 100644 index 000000000..9b4754b46 --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Tests/Fixtures/Controller/ExtendingRequest.php @@ -0,0 +1,18 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Tests\Fixtures\Controller; + +use Symfony\Component\HttpFoundation\Request; + +class ExtendingRequest extends Request +{ +} diff --git a/lib/silex/vendor/symfony/http-kernel/Tests/Fixtures/Controller/ExtendingSession.php b/lib/silex/vendor/symfony/http-kernel/Tests/Fixtures/Controller/ExtendingSession.php new file mode 100644 index 000000000..9fa54ee8b --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Tests/Fixtures/Controller/ExtendingSession.php @@ -0,0 +1,18 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Tests\Fixtures\Controller; + +use Symfony\Component\HttpFoundation\Session\Session; + +class ExtendingSession extends Session +{ +} diff --git a/lib/silex/vendor/symfony/http-kernel/Tests/Fixtures/Controller/NullableController.php b/lib/silex/vendor/symfony/http-kernel/Tests/Fixtures/Controller/NullableController.php new file mode 100644 index 000000000..9db4df7b4 --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Tests/Fixtures/Controller/NullableController.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\HttpKernel\Tests\Fixtures\Controller; + +class NullableController +{ + public function action(?string $foo, ?\stdClass $bar, ?string $baz = 'value', $mandatory) + { + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/Tests/Fixtures/Controller/VariadicController.php b/lib/silex/vendor/symfony/http-kernel/Tests/Fixtures/Controller/VariadicController.php new file mode 100644 index 000000000..c39812453 --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Tests/Fixtures/Controller/VariadicController.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\HttpKernel\Tests\Fixtures\Controller; + +class VariadicController +{ + public function action($foo, ...$bar) + { + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/Tests/Fixtures/DataCollector/CloneVarDataCollector.php b/lib/silex/vendor/symfony/http-kernel/Tests/Fixtures/DataCollector/CloneVarDataCollector.php new file mode 100644 index 000000000..89dec36af --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Tests/Fixtures/DataCollector/CloneVarDataCollector.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\HttpKernel\Tests\Fixtures\DataCollector; + +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\DataCollector\DataCollector; + +class CloneVarDataCollector extends DataCollector +{ + private $varToClone; + + public function __construct($varToClone) + { + $this->varToClone = $varToClone; + } + + public function collect(Request $request, Response $response, \Exception $exception = null) + { + $this->data = $this->cloneVar($this->varToClone); + } + + public function reset() + { + $this->data = array(); + } + + public function getData() + { + return $this->data; + } + + public function getName() + { + return 'clone_var'; + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/Tests/Fixtures/ExtensionAbsentBundle/ExtensionAbsentBundle.php b/lib/silex/vendor/symfony/http-kernel/Tests/Fixtures/ExtensionAbsentBundle/ExtensionAbsentBundle.php new file mode 100644 index 000000000..c8bfd36e6 --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Tests/Fixtures/ExtensionAbsentBundle/ExtensionAbsentBundle.php @@ -0,0 +1,18 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Tests\Fixtures\ExtensionAbsentBundle; + +use Symfony\Component\HttpKernel\Bundle\Bundle; + +class ExtensionAbsentBundle extends Bundle +{ +} diff --git a/lib/silex/vendor/symfony/http-kernel/Tests/Fixtures/ExtensionLoadedBundle/DependencyInjection/ExtensionLoadedExtension.php b/lib/silex/vendor/symfony/http-kernel/Tests/Fixtures/ExtensionLoadedBundle/DependencyInjection/ExtensionLoadedExtension.php new file mode 100644 index 000000000..b43bc665a --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Tests/Fixtures/ExtensionLoadedBundle/DependencyInjection/ExtensionLoadedExtension.php @@ -0,0 +1,22 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Tests\Fixtures\ExtensionLoadedBundle\DependencyInjection; + +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Extension\Extension; + +class ExtensionLoadedExtension extends Extension +{ + public function load(array $configs, ContainerBuilder $container) + { + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/Tests/Fixtures/ExtensionLoadedBundle/ExtensionLoadedBundle.php b/lib/silex/vendor/symfony/http-kernel/Tests/Fixtures/ExtensionLoadedBundle/ExtensionLoadedBundle.php new file mode 100644 index 000000000..3af81cb07 --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Tests/Fixtures/ExtensionLoadedBundle/ExtensionLoadedBundle.php @@ -0,0 +1,18 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Tests\Fixtures\ExtensionLoadedBundle; + +use Symfony\Component\HttpKernel\Bundle\Bundle; + +class ExtensionLoadedBundle extends Bundle +{ +} diff --git a/lib/silex/vendor/symfony/http-kernel/Tests/Fixtures/ExtensionNotValidBundle/DependencyInjection/ExtensionNotValidExtension.php b/lib/silex/vendor/symfony/http-kernel/Tests/Fixtures/ExtensionNotValidBundle/DependencyInjection/ExtensionNotValidExtension.php new file mode 100644 index 000000000..0fd64316f --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Tests/Fixtures/ExtensionNotValidBundle/DependencyInjection/ExtensionNotValidExtension.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\HttpKernel\Tests\Fixtures\ExtensionNotValidBundle\DependencyInjection; + +class ExtensionNotValidExtension +{ + public function getAlias() + { + return 'extension_not_valid'; + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/Tests/Fixtures/ExtensionNotValidBundle/ExtensionNotValidBundle.php b/lib/silex/vendor/symfony/http-kernel/Tests/Fixtures/ExtensionNotValidBundle/ExtensionNotValidBundle.php new file mode 100644 index 000000000..34e292039 --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Tests/Fixtures/ExtensionNotValidBundle/ExtensionNotValidBundle.php @@ -0,0 +1,18 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Tests\Fixtures\ExtensionNotValidBundle; + +use Symfony\Component\HttpKernel\Bundle\Bundle; + +class ExtensionNotValidBundle extends Bundle +{ +} diff --git a/lib/silex/vendor/symfony/http-kernel/Tests/Fixtures/ExtensionPresentBundle/Command/BarCommand.php b/lib/silex/vendor/symfony/http-kernel/Tests/Fixtures/ExtensionPresentBundle/Command/BarCommand.php new file mode 100644 index 000000000..977976b75 --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Tests/Fixtures/ExtensionPresentBundle/Command/BarCommand.php @@ -0,0 +1,17 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Tests\Fixtures\ExtensionPresentBundle\Command; + +use Symfony\Component\Console\Command\Command; + +class FooCommand extends Command +{ + protected function configure() + { + $this->setName('foo'); + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/Tests/Fixtures/ExtensionPresentBundle/DependencyInjection/ExtensionPresentExtension.php b/lib/silex/vendor/symfony/http-kernel/Tests/Fixtures/ExtensionPresentBundle/DependencyInjection/ExtensionPresentExtension.php new file mode 100644 index 000000000..108571718 --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Tests/Fixtures/ExtensionPresentBundle/DependencyInjection/ExtensionPresentExtension.php @@ -0,0 +1,22 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Tests\Fixtures\ExtensionPresentBundle\DependencyInjection; + +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Extension\Extension; + +class ExtensionPresentExtension extends Extension +{ + public function load(array $configs, ContainerBuilder $container) + { + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/Tests/Fixtures/ExtensionPresentBundle/ExtensionPresentBundle.php b/lib/silex/vendor/symfony/http-kernel/Tests/Fixtures/ExtensionPresentBundle/ExtensionPresentBundle.php new file mode 100644 index 000000000..36a7ad404 --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Tests/Fixtures/ExtensionPresentBundle/ExtensionPresentBundle.php @@ -0,0 +1,18 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Tests\Fixtures\ExtensionPresentBundle; + +use Symfony\Component\HttpKernel\Bundle\Bundle; + +class ExtensionPresentBundle extends Bundle +{ +} diff --git a/lib/silex/vendor/symfony/http-kernel/Tests/Fixtures/KernelForOverrideName.php b/lib/silex/vendor/symfony/http-kernel/Tests/Fixtures/KernelForOverrideName.php new file mode 100644 index 000000000..a1102ab78 --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Tests/Fixtures/KernelForOverrideName.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\HttpKernel\Tests\Fixtures; + +use Symfony\Component\HttpKernel\Kernel; +use Symfony\Component\Config\Loader\LoaderInterface; + +class KernelForOverrideName extends Kernel +{ + protected $name = 'overridden'; + + public function registerBundles() + { + } + + public function registerContainerConfiguration(LoaderInterface $loader) + { + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/Tests/Fixtures/KernelForTest.php b/lib/silex/vendor/symfony/http-kernel/Tests/Fixtures/KernelForTest.php new file mode 100644 index 000000000..5fd61bbc7 --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Tests/Fixtures/KernelForTest.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\HttpKernel\Tests\Fixtures; + +use Symfony\Component\HttpKernel\Kernel; +use Symfony\Component\Config\Loader\LoaderInterface; + +class KernelForTest extends Kernel +{ + public function getBundleMap() + { + return $this->bundleMap; + } + + public function registerBundles() + { + return array(); + } + + public function registerContainerConfiguration(LoaderInterface $loader) + { + } + + public function isBooted() + { + return $this->booted; + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/Tests/Fixtures/KernelWithoutBundles.php b/lib/silex/vendor/symfony/http-kernel/Tests/Fixtures/KernelWithoutBundles.php new file mode 100644 index 000000000..cee1b09fb --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Tests/Fixtures/KernelWithoutBundles.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\HttpKernel\Tests\Fixtures; + +use Symfony\Component\Config\Loader\LoaderInterface; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\HttpKernel\Kernel; + +class KernelWithoutBundles extends Kernel +{ + public function registerBundles() + { + return array(); + } + + public function registerContainerConfiguration(LoaderInterface $loader) + { + } + + protected function build(ContainerBuilder $container) + { + $container->setParameter('test_executed', true); + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/Tests/Fixtures/ResettableService.php b/lib/silex/vendor/symfony/http-kernel/Tests/Fixtures/ResettableService.php new file mode 100644 index 000000000..ffb72a35a --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Tests/Fixtures/ResettableService.php @@ -0,0 +1,13 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Tests\Fixtures; + +use Symfony\Component\HttpKernel\Client; + +class TestClient extends Client +{ + protected function getScript($request) + { + $script = parent::getScript($request); + + $autoload = file_exists(__DIR__.'/../../vendor/autoload.php') + ? __DIR__.'/../../vendor/autoload.php' + : __DIR__.'/../../../../../../vendor/autoload.php' + ; + + $script = preg_replace('/(\->register\(\);)/', "$0\nrequire_once '$autoload';\n", $script); + + return $script; + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/Tests/Fixtures/TestEventDispatcher.php b/lib/silex/vendor/symfony/http-kernel/Tests/Fixtures/TestEventDispatcher.php new file mode 100644 index 000000000..ca2e6a693 --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Tests/Fixtures/TestEventDispatcher.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\HttpKernel\Tests\Fixtures; + +use Symfony\Component\EventDispatcher\Debug\TraceableEventDispatcherInterface; +use Symfony\Component\EventDispatcher\EventDispatcher; + +class TestEventDispatcher extends EventDispatcher implements TraceableEventDispatcherInterface +{ + public function getCalledListeners() + { + return array('foo'); + } + + public function getNotCalledListeners() + { + return array('bar'); + } + + public function reset() + { + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/Tests/Fragment/EsiFragmentRendererTest.php b/lib/silex/vendor/symfony/http-kernel/Tests/Fragment/EsiFragmentRendererTest.php new file mode 100644 index 000000000..7099b4d7e --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Tests/Fragment/EsiFragmentRendererTest.php @@ -0,0 +1,118 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Tests\Fragment; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\HttpKernel\Controller\ControllerReference; +use Symfony\Component\HttpKernel\Fragment\EsiFragmentRenderer; +use Symfony\Component\HttpKernel\HttpCache\Esi; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpKernel\UriSigner; + +class EsiFragmentRendererTest extends TestCase +{ + public function testRenderFallbackToInlineStrategyIfEsiNotSupported() + { + $strategy = new EsiFragmentRenderer(new Esi(), $this->getInlineStrategy(true)); + $strategy->render('/', Request::create('/')); + } + + /** + * @group legacy + * @expectedDeprecation Passing non-scalar values as part of URI attributes to the ESI and SSI rendering strategies is deprecated %s. + */ + public function testRenderFallbackWithObjectAttributesIsDeprecated() + { + $strategy = new EsiFragmentRenderer(new Esi(), $this->getInlineStrategy(true), new UriSigner('foo')); + $request = Request::create('/'); + $reference = new ControllerReference('main_controller', array('foo' => new \stdClass()), array()); + $strategy->render($reference, $request); + } + + public function testRenderFallbackWithScalarIsNotDeprecated() + { + $strategy = new EsiFragmentRenderer(new Esi(), $this->getInlineStrategy(true), new UriSigner('foo')); + $request = Request::create('/'); + $reference = new ControllerReference('main_controller', array('foo' => array(true)), array()); + $strategy->render($reference, $request); + } + + public function testRender() + { + $strategy = new EsiFragmentRenderer(new Esi(), $this->getInlineStrategy()); + + $request = Request::create('/'); + $request->setLocale('fr'); + $request->headers->set('Surrogate-Capability', 'ESI/1.0'); + + $this->assertEquals('', $strategy->render('/', $request)->getContent()); + $this->assertEquals("\n", $strategy->render('/', $request, array('comment' => 'This is a comment'))->getContent()); + $this->assertEquals('', $strategy->render('/', $request, array('alt' => 'foo'))->getContent()); + } + + public function testRenderControllerReference() + { + $signer = new UriSigner('foo'); + $strategy = new EsiFragmentRenderer(new Esi(), $this->getInlineStrategy(), $signer); + + $request = Request::create('/'); + $request->setLocale('fr'); + $request->headers->set('Surrogate-Capability', 'ESI/1.0'); + + $reference = new ControllerReference('main_controller', array(), array()); + $altReference = new ControllerReference('alt_controller', array(), array()); + + $this->assertEquals( + '', + $strategy->render($reference, $request, array('alt' => $altReference))->getContent() + ); + } + + /** + * @expectedException \LogicException + */ + public function testRenderControllerReferenceWithoutSignerThrowsException() + { + $strategy = new EsiFragmentRenderer(new Esi(), $this->getInlineStrategy()); + + $request = Request::create('/'); + $request->setLocale('fr'); + $request->headers->set('Surrogate-Capability', 'ESI/1.0'); + + $strategy->render(new ControllerReference('main_controller'), $request); + } + + /** + * @expectedException \LogicException + */ + public function testRenderAltControllerReferenceWithoutSignerThrowsException() + { + $strategy = new EsiFragmentRenderer(new Esi(), $this->getInlineStrategy()); + + $request = Request::create('/'); + $request->setLocale('fr'); + $request->headers->set('Surrogate-Capability', 'ESI/1.0'); + + $strategy->render('/', $request, array('alt' => new ControllerReference('alt_controller'))); + } + + private function getInlineStrategy($called = false) + { + $inline = $this->getMockBuilder('Symfony\Component\HttpKernel\Fragment\InlineFragmentRenderer')->disableOriginalConstructor()->getMock(); + + if ($called) { + $inline->expects($this->once())->method('render'); + } + + return $inline; + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/Tests/Fragment/FragmentHandlerTest.php b/lib/silex/vendor/symfony/http-kernel/Tests/Fragment/FragmentHandlerTest.php new file mode 100644 index 000000000..9c906b50a --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Tests/Fragment/FragmentHandlerTest.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\HttpKernel\Tests\Fragment; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\HttpKernel\Fragment\FragmentHandler; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +/** + * @group time-sensitive + */ +class FragmentHandlerTest extends TestCase +{ + private $requestStack; + + protected function setUp() + { + $this->requestStack = $this->getMockBuilder('Symfony\\Component\\HttpFoundation\\RequestStack') + ->disableOriginalConstructor() + ->getMock() + ; + $this->requestStack + ->expects($this->any()) + ->method('getCurrentRequest') + ->will($this->returnValue(Request::create('/'))) + ; + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testRenderWhenRendererDoesNotExist() + { + $handler = new FragmentHandler($this->requestStack); + $handler->render('/', 'foo'); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testRenderWithUnknownRenderer() + { + $handler = $this->getHandler($this->returnValue(new Response('foo'))); + + $handler->render('/', 'bar'); + } + + /** + * @expectedException \RuntimeException + * @expectedExceptionMessage Error when rendering "http://localhost/" (Status code is 404). + */ + public function testDeliverWithUnsuccessfulResponse() + { + $handler = $this->getHandler($this->returnValue(new Response('foo', 404))); + + $handler->render('/', 'foo'); + } + + public function testRender() + { + $handler = $this->getHandler($this->returnValue(new Response('foo')), array('/', Request::create('/'), array('foo' => 'foo', 'ignore_errors' => true))); + + $this->assertEquals('foo', $handler->render('/', 'foo', array('foo' => 'foo'))); + } + + protected function getHandler($returnValue, $arguments = array()) + { + $renderer = $this->getMockBuilder('Symfony\Component\HttpKernel\Fragment\FragmentRendererInterface')->getMock(); + $renderer + ->expects($this->any()) + ->method('getName') + ->will($this->returnValue('foo')) + ; + $e = $renderer + ->expects($this->any()) + ->method('render') + ->will($returnValue) + ; + + if ($arguments) { + call_user_func_array(array($e, 'with'), $arguments); + } + + $handler = new FragmentHandler($this->requestStack); + $handler->addRenderer($renderer); + + return $handler; + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/Tests/Fragment/HIncludeFragmentRendererTest.php b/lib/silex/vendor/symfony/http-kernel/Tests/Fragment/HIncludeFragmentRendererTest.php new file mode 100644 index 000000000..1be052e5e --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Tests/Fragment/HIncludeFragmentRendererTest.php @@ -0,0 +1,89 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Tests\Fragment; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\HttpKernel\Controller\ControllerReference; +use Symfony\Component\HttpKernel\Fragment\HIncludeFragmentRenderer; +use Symfony\Component\HttpKernel\UriSigner; +use Symfony\Component\HttpFoundation\Request; + +class HIncludeFragmentRendererTest extends TestCase +{ + /** + * @expectedException \LogicException + */ + public function testRenderExceptionWhenControllerAndNoSigner() + { + $strategy = new HIncludeFragmentRenderer(); + $strategy->render(new ControllerReference('main_controller', array(), array()), Request::create('/')); + } + + public function testRenderWithControllerAndSigner() + { + $strategy = new HIncludeFragmentRenderer(null, new UriSigner('foo')); + + $this->assertEquals('', $strategy->render(new ControllerReference('main_controller', array(), array()), Request::create('/'))->getContent()); + } + + public function testRenderWithUri() + { + $strategy = new HIncludeFragmentRenderer(); + $this->assertEquals('', $strategy->render('/foo', Request::create('/'))->getContent()); + + $strategy = new HIncludeFragmentRenderer(null, new UriSigner('foo')); + $this->assertEquals('', $strategy->render('/foo', Request::create('/'))->getContent()); + } + + public function testRenderWithDefault() + { + // only default + $strategy = new HIncludeFragmentRenderer(); + $this->assertEquals('default', $strategy->render('/foo', Request::create('/'), array('default' => 'default'))->getContent()); + + // only global default + $strategy = new HIncludeFragmentRenderer(null, null, 'global_default'); + $this->assertEquals('global_default', $strategy->render('/foo', Request::create('/'), array())->getContent()); + + // global default and default + $strategy = new HIncludeFragmentRenderer(null, null, 'global_default'); + $this->assertEquals('default', $strategy->render('/foo', Request::create('/'), array('default' => 'default'))->getContent()); + } + + public function testRenderWithAttributesOptions() + { + // with id + $strategy = new HIncludeFragmentRenderer(); + $this->assertEquals('default', $strategy->render('/foo', Request::create('/'), array('default' => 'default', 'id' => 'bar'))->getContent()); + + // with attributes + $strategy = new HIncludeFragmentRenderer(); + $this->assertEquals('default', $strategy->render('/foo', Request::create('/'), array('default' => 'default', 'attributes' => array('p1' => 'v1', 'p2' => 'v2')))->getContent()); + + // with id & attributes + $strategy = new HIncludeFragmentRenderer(); + $this->assertEquals('default', $strategy->render('/foo', Request::create('/'), array('default' => 'default', 'id' => 'bar', 'attributes' => array('p1' => 'v1', 'p2' => 'v2')))->getContent()); + } + + public function testRenderWithDefaultText() + { + $engine = $this->getMockBuilder('Symfony\\Component\\Templating\\EngineInterface')->getMock(); + $engine->expects($this->once()) + ->method('exists') + ->with('default') + ->will($this->throwException(new \InvalidArgumentException())); + + // only default + $strategy = new HIncludeFragmentRenderer($engine); + $this->assertEquals('default', $strategy->render('/foo', Request::create('/'), array('default' => 'default'))->getContent()); + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/Tests/Fragment/InlineFragmentRendererTest.php b/lib/silex/vendor/symfony/http-kernel/Tests/Fragment/InlineFragmentRendererTest.php new file mode 100644 index 000000000..18e55a5be --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Tests/Fragment/InlineFragmentRendererTest.php @@ -0,0 +1,250 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Tests\Fragment; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\HttpFoundation\RequestStack; +use Symfony\Component\HttpKernel\Controller\ArgumentResolver; +use Symfony\Component\HttpKernel\Controller\ControllerResolverInterface; +use Symfony\Component\HttpKernel\Controller\ControllerReference; +use Symfony\Component\HttpKernel\HttpKernel; +use Symfony\Component\HttpKernel\Fragment\InlineFragmentRenderer; +use Symfony\Component\HttpKernel\KernelEvents; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\EventDispatcher\EventDispatcher; + +class InlineFragmentRendererTest extends TestCase +{ + public function testRender() + { + $strategy = new InlineFragmentRenderer($this->getKernel($this->returnValue(new Response('foo')))); + + $this->assertEquals('foo', $strategy->render('/', Request::create('/'))->getContent()); + } + + public function testRenderWithControllerReference() + { + $strategy = new InlineFragmentRenderer($this->getKernel($this->returnValue(new Response('foo')))); + + $this->assertEquals('foo', $strategy->render(new ControllerReference('main_controller', array(), array()), Request::create('/'))->getContent()); + } + + public function testRenderWithObjectsAsAttributes() + { + $object = new \stdClass(); + + $subRequest = Request::create('/_fragment?_path=_format%3Dhtml%26_locale%3Den%26_controller%3Dmain_controller'); + $subRequest->attributes->replace(array('object' => $object, '_format' => 'html', '_controller' => 'main_controller', '_locale' => 'en')); + $subRequest->headers->set('x-forwarded-for', array('127.0.0.1')); + $subRequest->server->set('HTTP_X_FORWARDED_FOR', '127.0.0.1'); + + $strategy = new InlineFragmentRenderer($this->getKernelExpectingRequest($subRequest)); + + $this->assertSame('foo', $strategy->render(new ControllerReference('main_controller', array('object' => $object), array()), Request::create('/'))->getContent()); + } + + /** + * @group legacy + */ + public function testRenderWithObjectsAsAttributesPassedAsObjectsInTheControllerLegacy() + { + $resolver = $this->getMockBuilder('Symfony\\Component\\HttpKernel\\Controller\\ControllerResolver')->setMethods(array('getController'))->getMock(); + $resolver + ->expects($this->once()) + ->method('getController') + ->will($this->returnValue(function (\stdClass $object, Bar $object1) { + return new Response($object1->getBar()); + })) + ; + + $kernel = new HttpKernel(new EventDispatcher(), $resolver, new RequestStack()); + $renderer = new InlineFragmentRenderer($kernel); + + $response = $renderer->render(new ControllerReference('main_controller', array('object' => new \stdClass(), 'object1' => new Bar()), array()), Request::create('/')); + $this->assertEquals('bar', $response->getContent()); + } + + /** + * @group legacy + */ + public function testRenderWithObjectsAsAttributesPassedAsObjectsInTheController() + { + $resolver = $this->getMockBuilder(ControllerResolverInterface::class)->getMock(); + $resolver + ->expects($this->once()) + ->method('getController') + ->will($this->returnValue(function (\stdClass $object, Bar $object1) { + return new Response($object1->getBar()); + })) + ; + + $kernel = new HttpKernel(new EventDispatcher(), $resolver, new RequestStack(), new ArgumentResolver()); + $renderer = new InlineFragmentRenderer($kernel); + + $response = $renderer->render(new ControllerReference('main_controller', array('object' => new \stdClass(), 'object1' => new Bar()), array()), Request::create('/')); + $this->assertEquals('bar', $response->getContent()); + } + + public function testRenderWithTrustedHeaderDisabled() + { + Request::setTrustedProxies(array(), 0); + + $strategy = new InlineFragmentRenderer($this->getKernelExpectingRequest(Request::create('/'))); + $this->assertSame('foo', $strategy->render('/', Request::create('/'))->getContent()); + + Request::setTrustedProxies(array(), -1); + } + + /** + * @expectedException \RuntimeException + */ + public function testRenderExceptionNoIgnoreErrors() + { + $dispatcher = $this->getMockBuilder('Symfony\Component\EventDispatcher\EventDispatcherInterface')->getMock(); + $dispatcher->expects($this->never())->method('dispatch'); + + $strategy = new InlineFragmentRenderer($this->getKernel($this->throwException(new \RuntimeException('foo'))), $dispatcher); + + $this->assertEquals('foo', $strategy->render('/', Request::create('/'))->getContent()); + } + + public function testRenderExceptionIgnoreErrors() + { + $dispatcher = $this->getMockBuilder('Symfony\Component\EventDispatcher\EventDispatcherInterface')->getMock(); + $dispatcher->expects($this->once())->method('dispatch')->with(KernelEvents::EXCEPTION); + + $strategy = new InlineFragmentRenderer($this->getKernel($this->throwException(new \RuntimeException('foo'))), $dispatcher); + + $this->assertEmpty($strategy->render('/', Request::create('/'), array('ignore_errors' => true))->getContent()); + } + + public function testRenderExceptionIgnoreErrorsWithAlt() + { + $strategy = new InlineFragmentRenderer($this->getKernel($this->onConsecutiveCalls( + $this->throwException(new \RuntimeException('foo')), + $this->returnValue(new Response('bar')) + ))); + + $this->assertEquals('bar', $strategy->render('/', Request::create('/'), array('ignore_errors' => true, 'alt' => '/foo'))->getContent()); + } + + private function getKernel($returnValue) + { + $kernel = $this->getMockBuilder('Symfony\Component\HttpKernel\HttpKernelInterface')->getMock(); + $kernel + ->expects($this->any()) + ->method('handle') + ->will($returnValue) + ; + + return $kernel; + } + + public function testExceptionInSubRequestsDoesNotMangleOutputBuffers() + { + $controllerResolver = $this->getMockBuilder('Symfony\\Component\\HttpKernel\\Controller\\ControllerResolverInterface')->getMock(); + $controllerResolver + ->expects($this->once()) + ->method('getController') + ->will($this->returnValue(function () { + ob_start(); + echo 'bar'; + throw new \RuntimeException(); + })) + ; + + $argumentResolver = $this->getMockBuilder('Symfony\\Component\\HttpKernel\\Controller\\ArgumentResolverInterface')->getMock(); + $argumentResolver + ->expects($this->once()) + ->method('getArguments') + ->will($this->returnValue(array())) + ; + + $kernel = new HttpKernel(new EventDispatcher(), $controllerResolver, new RequestStack(), $argumentResolver); + $renderer = new InlineFragmentRenderer($kernel); + + // simulate a main request with output buffering + ob_start(); + echo 'Foo'; + + // simulate a sub-request with output buffering and an exception + $renderer->render('/', Request::create('/'), array('ignore_errors' => true)); + + $this->assertEquals('Foo', ob_get_clean()); + } + + public function testESIHeaderIsKeptInSubrequest() + { + $expectedSubRequest = Request::create('/'); + $expectedSubRequest->headers->set('Surrogate-Capability', 'abc="ESI/1.0"'); + + if (Request::HEADER_X_FORWARDED_FOR & Request::getTrustedHeaderSet()) { + $expectedSubRequest->headers->set('x-forwarded-for', array('127.0.0.1')); + $expectedSubRequest->server->set('HTTP_X_FORWARDED_FOR', '127.0.0.1'); + } + + $strategy = new InlineFragmentRenderer($this->getKernelExpectingRequest($expectedSubRequest)); + + $request = Request::create('/'); + $request->headers->set('Surrogate-Capability', 'abc="ESI/1.0"'); + $strategy->render('/', $request); + } + + public function testESIHeaderIsKeptInSubrequestWithTrustedHeaderDisabled() + { + Request::setTrustedProxies(array(), 0); + + $this->testESIHeaderIsKeptInSubrequest(); + + Request::setTrustedProxies(array(), -1); + } + + public function testHeadersPossiblyResultingIn304AreNotAssignedToSubrequest() + { + $expectedSubRequest = Request::create('/'); + if (Request::HEADER_X_FORWARDED_FOR & Request::getTrustedHeaderSet()) { + $expectedSubRequest->headers->set('x-forwarded-for', array('127.0.0.1')); + $expectedSubRequest->server->set('HTTP_X_FORWARDED_FOR', '127.0.0.1'); + } + + $strategy = new InlineFragmentRenderer($this->getKernelExpectingRequest($expectedSubRequest)); + $request = Request::create('/', 'GET', array(), array(), array(), array('HTTP_IF_MODIFIED_SINCE' => 'Fri, 01 Jan 2016 00:00:00 GMT', 'HTTP_IF_NONE_MATCH' => '*')); + $strategy->render('/', $request); + } + + /** + * Creates a Kernel expecting a request equals to $request + * Allows delta in comparison in case REQUEST_TIME changed by 1 second. + */ + private function getKernelExpectingRequest(Request $request, $strict = false) + { + $kernel = $this->getMockBuilder('Symfony\Component\HttpKernel\HttpKernelInterface')->getMock(); + $kernel + ->expects($this->once()) + ->method('handle') + ->with($this->equalTo($request, 1)) + ->willReturn(new Response('foo')); + + return $kernel; + } +} + +class Bar +{ + public $bar = 'bar'; + + public function getBar() + { + return $this->bar; + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/Tests/Fragment/RoutableFragmentRendererTest.php b/lib/silex/vendor/symfony/http-kernel/Tests/Fragment/RoutableFragmentRendererTest.php new file mode 100644 index 000000000..3a040dedd --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Tests/Fragment/RoutableFragmentRendererTest.php @@ -0,0 +1,94 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Tests\Fragment; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpKernel\Controller\ControllerReference; + +class RoutableFragmentRendererTest extends TestCase +{ + /** + * @dataProvider getGenerateFragmentUriData + */ + public function testGenerateFragmentUri($uri, $controller) + { + $this->assertEquals($uri, $this->callGenerateFragmentUriMethod($controller, Request::create('/'))); + } + + /** + * @dataProvider getGenerateFragmentUriData + */ + public function testGenerateAbsoluteFragmentUri($uri, $controller) + { + $this->assertEquals('http://localhost'.$uri, $this->callGenerateFragmentUriMethod($controller, Request::create('/'), true)); + } + + public function getGenerateFragmentUriData() + { + return array( + array('/_fragment?_path=_format%3Dhtml%26_locale%3Den%26_controller%3Dcontroller', new ControllerReference('controller', array(), array())), + array('/_fragment?_path=_format%3Dxml%26_locale%3Den%26_controller%3Dcontroller', new ControllerReference('controller', array('_format' => 'xml'), array())), + array('/_fragment?_path=foo%3Dfoo%26_format%3Djson%26_locale%3Den%26_controller%3Dcontroller', new ControllerReference('controller', array('foo' => 'foo', '_format' => 'json'), array())), + array('/_fragment?bar=bar&_path=foo%3Dfoo%26_format%3Dhtml%26_locale%3Den%26_controller%3Dcontroller', new ControllerReference('controller', array('foo' => 'foo'), array('bar' => 'bar'))), + array('/_fragment?foo=foo&_path=_format%3Dhtml%26_locale%3Den%26_controller%3Dcontroller', new ControllerReference('controller', array(), array('foo' => 'foo'))), + array('/_fragment?_path=foo%255B0%255D%3Dfoo%26foo%255B1%255D%3Dbar%26_format%3Dhtml%26_locale%3Den%26_controller%3Dcontroller', new ControllerReference('controller', array('foo' => array('foo', 'bar')), array())), + ); + } + + public function testGenerateFragmentUriWithARequest() + { + $request = Request::create('/'); + $request->attributes->set('_format', 'json'); + $request->setLocale('fr'); + $controller = new ControllerReference('controller', array(), array()); + + $this->assertEquals('/_fragment?_path=_format%3Djson%26_locale%3Dfr%26_controller%3Dcontroller', $this->callGenerateFragmentUriMethod($controller, $request)); + } + + /** + * @expectedException \LogicException + * @dataProvider getGenerateFragmentUriDataWithNonScalar + */ + public function testGenerateFragmentUriWithNonScalar($controller) + { + $this->callGenerateFragmentUriMethod($controller, Request::create('/')); + } + + public function getGenerateFragmentUriDataWithNonScalar() + { + return array( + array(new ControllerReference('controller', array('foo' => new Foo(), 'bar' => 'bar'), array())), + array(new ControllerReference('controller', array('foo' => array('foo' => 'foo'), 'bar' => array('bar' => new Foo())), array())), + ); + } + + private function callGenerateFragmentUriMethod(ControllerReference $reference, Request $request, $absolute = false) + { + $renderer = $this->getMockForAbstractClass('Symfony\Component\HttpKernel\Fragment\RoutableFragmentRenderer'); + $r = new \ReflectionObject($renderer); + $m = $r->getMethod('generateFragmentUri'); + $m->setAccessible(true); + + return $m->invoke($renderer, $reference, $request, $absolute); + } +} + +class Foo +{ + public $foo; + + public function getFoo() + { + return $this->foo; + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/Tests/Fragment/SsiFragmentRendererTest.php b/lib/silex/vendor/symfony/http-kernel/Tests/Fragment/SsiFragmentRendererTest.php new file mode 100644 index 000000000..b537625f2 --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Tests/Fragment/SsiFragmentRendererTest.php @@ -0,0 +1,97 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Tests\Fragment; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\HttpKernel\Controller\ControllerReference; +use Symfony\Component\HttpKernel\Fragment\SsiFragmentRenderer; +use Symfony\Component\HttpKernel\HttpCache\Ssi; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpKernel\UriSigner; + +class SsiFragmentRendererTest extends TestCase +{ + public function testRenderFallbackToInlineStrategyIfSsiNotSupported() + { + $strategy = new SsiFragmentRenderer(new Ssi(), $this->getInlineStrategy(true)); + $strategy->render('/', Request::create('/')); + } + + public function testRender() + { + $strategy = new SsiFragmentRenderer(new Ssi(), $this->getInlineStrategy()); + + $request = Request::create('/'); + $request->setLocale('fr'); + $request->headers->set('Surrogate-Capability', 'SSI/1.0'); + + $this->assertEquals('', $strategy->render('/', $request)->getContent()); + $this->assertEquals('', $strategy->render('/', $request, array('comment' => 'This is a comment'))->getContent(), 'Strategy options should not impact the ssi include tag'); + } + + public function testRenderControllerReference() + { + $signer = new UriSigner('foo'); + $strategy = new SsiFragmentRenderer(new Ssi(), $this->getInlineStrategy(), $signer); + + $request = Request::create('/'); + $request->setLocale('fr'); + $request->headers->set('Surrogate-Capability', 'SSI/1.0'); + + $reference = new ControllerReference('main_controller', array(), array()); + $altReference = new ControllerReference('alt_controller', array(), array()); + + $this->assertEquals( + '', + $strategy->render($reference, $request, array('alt' => $altReference))->getContent() + ); + } + + /** + * @expectedException \LogicException + */ + public function testRenderControllerReferenceWithoutSignerThrowsException() + { + $strategy = new SsiFragmentRenderer(new Ssi(), $this->getInlineStrategy()); + + $request = Request::create('/'); + $request->setLocale('fr'); + $request->headers->set('Surrogate-Capability', 'SSI/1.0'); + + $strategy->render(new ControllerReference('main_controller'), $request); + } + + /** + * @expectedException \LogicException + */ + public function testRenderAltControllerReferenceWithoutSignerThrowsException() + { + $strategy = new SsiFragmentRenderer(new Ssi(), $this->getInlineStrategy()); + + $request = Request::create('/'); + $request->setLocale('fr'); + $request->headers->set('Surrogate-Capability', 'SSI/1.0'); + + $strategy->render('/', $request, array('alt' => new ControllerReference('alt_controller'))); + } + + private function getInlineStrategy($called = false) + { + $inline = $this->getMockBuilder('Symfony\Component\HttpKernel\Fragment\InlineFragmentRenderer')->disableOriginalConstructor()->getMock(); + + if ($called) { + $inline->expects($this->once())->method('render'); + } + + return $inline; + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/Tests/HttpCache/EsiTest.php b/lib/silex/vendor/symfony/http-kernel/Tests/HttpCache/EsiTest.php new file mode 100644 index 000000000..a8662b2a2 --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Tests/HttpCache/EsiTest.php @@ -0,0 +1,248 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Tests\HttpCache; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\HttpKernel\HttpCache\Esi; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +class EsiTest extends TestCase +{ + public function testHasSurrogateEsiCapability() + { + $esi = new Esi(); + + $request = Request::create('/'); + $request->headers->set('Surrogate-Capability', 'abc="ESI/1.0"'); + $this->assertTrue($esi->hasSurrogateCapability($request)); + + $request = Request::create('/'); + $request->headers->set('Surrogate-Capability', 'foobar'); + $this->assertFalse($esi->hasSurrogateCapability($request)); + + $request = Request::create('/'); + $this->assertFalse($esi->hasSurrogateCapability($request)); + } + + public function testAddSurrogateEsiCapability() + { + $esi = new Esi(); + + $request = Request::create('/'); + $esi->addSurrogateCapability($request); + $this->assertEquals('symfony="ESI/1.0"', $request->headers->get('Surrogate-Capability')); + + $esi->addSurrogateCapability($request); + $this->assertEquals('symfony="ESI/1.0", symfony="ESI/1.0"', $request->headers->get('Surrogate-Capability')); + } + + public function testAddSurrogateControl() + { + $esi = new Esi(); + + $response = new Response('foo '); + $esi->addSurrogateControl($response); + $this->assertEquals('content="ESI/1.0"', $response->headers->get('Surrogate-Control')); + + $response = new Response('foo'); + $esi->addSurrogateControl($response); + $this->assertEquals('', $response->headers->get('Surrogate-Control')); + } + + public function testNeedsEsiParsing() + { + $esi = new Esi(); + + $response = new Response(); + $response->headers->set('Surrogate-Control', 'content="ESI/1.0"'); + $this->assertTrue($esi->needsParsing($response)); + + $response = new Response(); + $this->assertFalse($esi->needsParsing($response)); + } + + public function testRenderIncludeTag() + { + $esi = new Esi(); + + $this->assertEquals('', $esi->renderIncludeTag('/', '/alt', true)); + $this->assertEquals('', $esi->renderIncludeTag('/', '/alt', false)); + $this->assertEquals('', $esi->renderIncludeTag('/')); + $this->assertEquals(''."\n".'', $esi->renderIncludeTag('/', '/alt', true, 'some comment')); + } + + public function testProcessDoesNothingIfContentTypeIsNotHtml() + { + $esi = new Esi(); + + $request = Request::create('/'); + $response = new Response(); + $response->headers->set('Content-Type', 'text/plain'); + $esi->process($request, $response); + + $this->assertFalse($response->headers->has('x-body-eval')); + } + + public function testMultilineEsiRemoveTagsAreRemoved() + { + $esi = new Esi(); + + $request = Request::create('/'); + $response = new Response(' www.example.com Keep this'."\n www.example.com And this"); + $esi->process($request, $response); + + $this->assertEquals(' Keep this And this', $response->getContent()); + } + + public function testCommentTagsAreRemoved() + { + $esi = new Esi(); + + $request = Request::create('/'); + $response = new Response(' Keep this'); + $esi->process($request, $response); + + $this->assertEquals(' Keep this', $response->getContent()); + } + + public function testProcess() + { + $esi = new Esi(); + + $request = Request::create('/'); + $response = new Response('foo '); + $esi->process($request, $response); + + $this->assertEquals('foo surrogate->handle($this, \'...\', \'alt\', true) ?>'."\n", $response->getContent()); + $this->assertEquals('ESI', $response->headers->get('x-body-eval')); + + $response = new Response('foo '); + $esi->process($request, $response); + + $this->assertEquals('foo surrogate->handle($this, \'foo\\\'\', \'bar\\\'\', true) ?>'."\n", $response->getContent()); + + $response = new Response('foo '); + $esi->process($request, $response); + + $this->assertEquals('foo surrogate->handle($this, \'...\', \'\', false) ?>'."\n", $response->getContent()); + + $response = new Response('foo '); + $esi->process($request, $response); + + $this->assertEquals('foo surrogate->handle($this, \'...\', \'\', false) ?>'."\n", $response->getContent()); + } + + public function testProcessEscapesPhpTags() + { + $esi = new Esi(); + + $request = Request::create('/'); + $response = new Response(''); + $esi->process($request, $response); + + $this->assertEquals('php cript language=php>', $response->getContent()); + } + + /** + * @expectedException \RuntimeException + */ + public function testProcessWhenNoSrcInAnEsi() + { + $esi = new Esi(); + + $request = Request::create('/'); + $response = new Response('foo '); + $esi->process($request, $response); + } + + public function testProcessRemoveSurrogateControlHeader() + { + $esi = new Esi(); + + $request = Request::create('/'); + $response = new Response('foo '); + $response->headers->set('Surrogate-Control', 'content="ESI/1.0"'); + $esi->process($request, $response); + $this->assertEquals('ESI', $response->headers->get('x-body-eval')); + + $response->headers->set('Surrogate-Control', 'no-store, content="ESI/1.0"'); + $esi->process($request, $response); + $this->assertEquals('ESI', $response->headers->get('x-body-eval')); + $this->assertEquals('no-store', $response->headers->get('surrogate-control')); + + $response->headers->set('Surrogate-Control', 'content="ESI/1.0", no-store'); + $esi->process($request, $response); + $this->assertEquals('ESI', $response->headers->get('x-body-eval')); + $this->assertEquals('no-store', $response->headers->get('surrogate-control')); + } + + public function testHandle() + { + $esi = new Esi(); + $cache = $this->getCache(Request::create('/'), new Response('foo')); + $this->assertEquals('foo', $esi->handle($cache, '/', '/alt', true)); + } + + /** + * @expectedException \RuntimeException + */ + public function testHandleWhenResponseIsNot200() + { + $esi = new Esi(); + $response = new Response('foo'); + $response->setStatusCode(404); + $cache = $this->getCache(Request::create('/'), $response); + $esi->handle($cache, '/', '/alt', false); + } + + public function testHandleWhenResponseIsNot200AndErrorsAreIgnored() + { + $esi = new Esi(); + $response = new Response('foo'); + $response->setStatusCode(404); + $cache = $this->getCache(Request::create('/'), $response); + $this->assertEquals('', $esi->handle($cache, '/', '/alt', true)); + } + + public function testHandleWhenResponseIsNot200AndAltIsPresent() + { + $esi = new Esi(); + $response1 = new Response('foo'); + $response1->setStatusCode(404); + $response2 = new Response('bar'); + $cache = $this->getCache(Request::create('/'), array($response1, $response2)); + $this->assertEquals('bar', $esi->handle($cache, '/', '/alt', false)); + } + + protected function getCache($request, $response) + { + $cache = $this->getMockBuilder('Symfony\Component\HttpKernel\HttpCache\HttpCache')->setMethods(array('getRequest', 'handle'))->disableOriginalConstructor()->getMock(); + $cache->expects($this->any()) + ->method('getRequest') + ->will($this->returnValue($request)) + ; + if (is_array($response)) { + $cache->expects($this->any()) + ->method('handle') + ->will(call_user_func_array(array($this, 'onConsecutiveCalls'), $response)) + ; + } else { + $cache->expects($this->any()) + ->method('handle') + ->will($this->returnValue($response)) + ; + } + + return $cache; + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/Tests/HttpCache/HttpCacheTest.php b/lib/silex/vendor/symfony/http-kernel/Tests/HttpCache/HttpCacheTest.php new file mode 100644 index 000000000..2a9a30d9c --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Tests/HttpCache/HttpCacheTest.php @@ -0,0 +1,1482 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Tests\HttpCache; + +use Symfony\Component\HttpKernel\HttpCache\HttpCache; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\HttpKernelInterface; + +/** + * @group time-sensitive + */ +class HttpCacheTest extends HttpCacheTestCase +{ + public function testTerminateDelegatesTerminationOnlyForTerminableInterface() + { + $storeMock = $this->getMockBuilder('Symfony\\Component\\HttpKernel\\HttpCache\\StoreInterface') + ->disableOriginalConstructor() + ->getMock(); + + // does not implement TerminableInterface + $kernel = new TestKernel(); + $httpCache = new HttpCache($kernel, $storeMock); + $httpCache->terminate(Request::create('/'), new Response()); + + $this->assertFalse($kernel->terminateCalled, 'terminate() is never called if the kernel class does not implement TerminableInterface'); + + // implements TerminableInterface + $kernelMock = $this->getMockBuilder('Symfony\\Component\\HttpKernel\\Kernel') + ->disableOriginalConstructor() + ->setMethods(array('terminate', 'registerBundles', 'registerContainerConfiguration')) + ->getMock(); + + $kernelMock->expects($this->once()) + ->method('terminate'); + + $kernel = new HttpCache($kernelMock, $storeMock); + $kernel->terminate(Request::create('/'), new Response()); + } + + public function testPassesOnNonGetHeadRequests() + { + $this->setNextResponse(200); + $this->request('POST', '/'); + $this->assertHttpKernelIsCalled(); + $this->assertResponseOk(); + $this->assertTraceContains('pass'); + $this->assertFalse($this->response->headers->has('Age')); + } + + public function testInvalidatesOnPostPutDeleteRequests() + { + foreach (array('post', 'put', 'delete') as $method) { + $this->setNextResponse(200); + $this->request($method, '/'); + + $this->assertHttpKernelIsCalled(); + $this->assertResponseOk(); + $this->assertTraceContains('invalidate'); + $this->assertTraceContains('pass'); + } + } + + public function testDoesNotCacheWithAuthorizationRequestHeaderAndNonPublicResponse() + { + $this->setNextResponse(200, array('ETag' => '"Foo"')); + $this->request('GET', '/', array('HTTP_AUTHORIZATION' => 'basic foobarbaz')); + + $this->assertHttpKernelIsCalled(); + $this->assertResponseOk(); + $this->assertEquals('private', $this->response->headers->get('Cache-Control')); + + $this->assertTraceContains('miss'); + $this->assertTraceNotContains('store'); + $this->assertFalse($this->response->headers->has('Age')); + } + + public function testDoesCacheWithAuthorizationRequestHeaderAndPublicResponse() + { + $this->setNextResponse(200, array('Cache-Control' => 'public', 'ETag' => '"Foo"')); + $this->request('GET', '/', array('HTTP_AUTHORIZATION' => 'basic foobarbaz')); + + $this->assertHttpKernelIsCalled(); + $this->assertResponseOk(); + $this->assertTraceContains('miss'); + $this->assertTraceContains('store'); + $this->assertTrue($this->response->headers->has('Age')); + $this->assertEquals('public', $this->response->headers->get('Cache-Control')); + } + + public function testDoesNotCacheWithCookieHeaderAndNonPublicResponse() + { + $this->setNextResponse(200, array('ETag' => '"Foo"')); + $this->request('GET', '/', array(), array('foo' => 'bar')); + + $this->assertHttpKernelIsCalled(); + $this->assertResponseOk(); + $this->assertEquals('private', $this->response->headers->get('Cache-Control')); + $this->assertTraceContains('miss'); + $this->assertTraceNotContains('store'); + $this->assertFalse($this->response->headers->has('Age')); + } + + public function testDoesNotCacheRequestsWithACookieHeader() + { + $this->setNextResponse(200); + $this->request('GET', '/', array(), array('foo' => 'bar')); + + $this->assertHttpKernelIsCalled(); + $this->assertResponseOk(); + $this->assertEquals('private', $this->response->headers->get('Cache-Control')); + $this->assertTraceContains('miss'); + $this->assertTraceNotContains('store'); + $this->assertFalse($this->response->headers->has('Age')); + } + + public function testRespondsWith304WhenIfModifiedSinceMatchesLastModified() + { + $time = \DateTime::createFromFormat('U', time()); + + $this->setNextResponse(200, array('Cache-Control' => 'public', 'Last-Modified' => $time->format(DATE_RFC2822), 'Content-Type' => 'text/plain'), 'Hello World'); + $this->request('GET', '/', array('HTTP_IF_MODIFIED_SINCE' => $time->format(DATE_RFC2822))); + + $this->assertHttpKernelIsCalled(); + $this->assertEquals(304, $this->response->getStatusCode()); + $this->assertEquals('', $this->response->headers->get('Content-Type')); + $this->assertEmpty($this->response->getContent()); + $this->assertTraceContains('miss'); + $this->assertTraceContains('store'); + } + + public function testRespondsWith304WhenIfNoneMatchMatchesETag() + { + $this->setNextResponse(200, array('Cache-Control' => 'public', 'ETag' => '12345', 'Content-Type' => 'text/plain'), 'Hello World'); + $this->request('GET', '/', array('HTTP_IF_NONE_MATCH' => '12345')); + + $this->assertHttpKernelIsCalled(); + $this->assertEquals(304, $this->response->getStatusCode()); + $this->assertEquals('', $this->response->headers->get('Content-Type')); + $this->assertTrue($this->response->headers->has('ETag')); + $this->assertEmpty($this->response->getContent()); + $this->assertTraceContains('miss'); + $this->assertTraceContains('store'); + } + + public function testRespondsWith304OnlyIfIfNoneMatchAndIfModifiedSinceBothMatch() + { + $time = \DateTime::createFromFormat('U', time()); + + $this->setNextResponse(200, array(), '', function ($request, $response) use ($time) { + $response->setStatusCode(200); + $response->headers->set('ETag', '12345'); + $response->headers->set('Last-Modified', $time->format(DATE_RFC2822)); + $response->headers->set('Content-Type', 'text/plain'); + $response->setContent('Hello World'); + }); + + // only ETag matches + $t = \DateTime::createFromFormat('U', time() - 3600); + $this->request('GET', '/', array('HTTP_IF_NONE_MATCH' => '12345', 'HTTP_IF_MODIFIED_SINCE' => $t->format(DATE_RFC2822))); + $this->assertHttpKernelIsCalled(); + $this->assertEquals(200, $this->response->getStatusCode()); + + // only Last-Modified matches + $this->request('GET', '/', array('HTTP_IF_NONE_MATCH' => '1234', 'HTTP_IF_MODIFIED_SINCE' => $time->format(DATE_RFC2822))); + $this->assertHttpKernelIsCalled(); + $this->assertEquals(200, $this->response->getStatusCode()); + + // Both matches + $this->request('GET', '/', array('HTTP_IF_NONE_MATCH' => '12345', 'HTTP_IF_MODIFIED_SINCE' => $time->format(DATE_RFC2822))); + $this->assertHttpKernelIsCalled(); + $this->assertEquals(304, $this->response->getStatusCode()); + } + + public function testIncrementsMaxAgeWhenNoDateIsSpecifiedEventWhenUsingETag() + { + $this->setNextResponse( + 200, + array( + 'ETag' => '1234', + 'Cache-Control' => 'public, s-maxage=60', + ) + ); + + $this->request('GET', '/'); + $this->assertHttpKernelIsCalled(); + $this->assertEquals(200, $this->response->getStatusCode()); + $this->assertTraceContains('miss'); + $this->assertTraceContains('store'); + + sleep(2); + + $this->request('GET', '/'); + $this->assertHttpKernelIsNotCalled(); + $this->assertEquals(200, $this->response->getStatusCode()); + $this->assertTraceContains('fresh'); + $this->assertEquals(2, $this->response->headers->get('Age')); + } + + public function testValidatesPrivateResponsesCachedOnTheClient() + { + $this->setNextResponse(200, array(), '', function ($request, $response) { + $etags = preg_split('/\s*,\s*/', $request->headers->get('IF_NONE_MATCH')); + if ($request->cookies->has('authenticated')) { + $response->headers->set('Cache-Control', 'private, no-store'); + $response->setETag('"private tag"'); + if (in_array('"private tag"', $etags)) { + $response->setStatusCode(304); + } else { + $response->setStatusCode(200); + $response->headers->set('Content-Type', 'text/plain'); + $response->setContent('private data'); + } + } else { + $response->headers->set('Cache-Control', 'public'); + $response->setETag('"public tag"'); + if (in_array('"public tag"', $etags)) { + $response->setStatusCode(304); + } else { + $response->setStatusCode(200); + $response->headers->set('Content-Type', 'text/plain'); + $response->setContent('public data'); + } + } + }); + + $this->request('GET', '/'); + $this->assertHttpKernelIsCalled(); + $this->assertEquals(200, $this->response->getStatusCode()); + $this->assertEquals('"public tag"', $this->response->headers->get('ETag')); + $this->assertEquals('public data', $this->response->getContent()); + $this->assertTraceContains('miss'); + $this->assertTraceContains('store'); + + $this->request('GET', '/', array(), array('authenticated' => '')); + $this->assertHttpKernelIsCalled(); + $this->assertEquals(200, $this->response->getStatusCode()); + $this->assertEquals('"private tag"', $this->response->headers->get('ETag')); + $this->assertEquals('private data', $this->response->getContent()); + $this->assertTraceContains('stale'); + $this->assertTraceContains('invalid'); + $this->assertTraceNotContains('store'); + } + + public function testStoresResponsesWhenNoCacheRequestDirectivePresent() + { + $time = \DateTime::createFromFormat('U', time() + 5); + + $this->setNextResponse(200, array('Cache-Control' => 'public', 'Expires' => $time->format(DATE_RFC2822))); + $this->request('GET', '/', array('HTTP_CACHE_CONTROL' => 'no-cache')); + + $this->assertHttpKernelIsCalled(); + $this->assertTraceContains('store'); + $this->assertTrue($this->response->headers->has('Age')); + } + + public function testReloadsResponsesWhenCacheHitsButNoCacheRequestDirectivePresentWhenAllowReloadIsSetTrue() + { + $count = 0; + + $this->setNextResponse(200, array('Cache-Control' => 'public, max-age=10000'), '', function ($request, $response) use (&$count) { + ++$count; + $response->setContent(1 == $count ? 'Hello World' : 'Goodbye World'); + }); + + $this->request('GET', '/'); + $this->assertEquals(200, $this->response->getStatusCode()); + $this->assertEquals('Hello World', $this->response->getContent()); + $this->assertTraceContains('store'); + + $this->request('GET', '/'); + $this->assertEquals(200, $this->response->getStatusCode()); + $this->assertEquals('Hello World', $this->response->getContent()); + $this->assertTraceContains('fresh'); + + $this->cacheConfig['allow_reload'] = true; + $this->request('GET', '/', array('HTTP_CACHE_CONTROL' => 'no-cache')); + $this->assertEquals(200, $this->response->getStatusCode()); + $this->assertEquals('Goodbye World', $this->response->getContent()); + $this->assertTraceContains('reload'); + $this->assertTraceContains('store'); + } + + public function testDoesNotReloadResponsesWhenAllowReloadIsSetFalseDefault() + { + $count = 0; + + $this->setNextResponse(200, array('Cache-Control' => 'public, max-age=10000'), '', function ($request, $response) use (&$count) { + ++$count; + $response->setContent(1 == $count ? 'Hello World' : 'Goodbye World'); + }); + + $this->request('GET', '/'); + $this->assertEquals(200, $this->response->getStatusCode()); + $this->assertEquals('Hello World', $this->response->getContent()); + $this->assertTraceContains('store'); + + $this->request('GET', '/'); + $this->assertEquals(200, $this->response->getStatusCode()); + $this->assertEquals('Hello World', $this->response->getContent()); + $this->assertTraceContains('fresh'); + + $this->cacheConfig['allow_reload'] = false; + $this->request('GET', '/', array('HTTP_CACHE_CONTROL' => 'no-cache')); + $this->assertEquals(200, $this->response->getStatusCode()); + $this->assertEquals('Hello World', $this->response->getContent()); + $this->assertTraceNotContains('reload'); + + $this->request('GET', '/', array('HTTP_CACHE_CONTROL' => 'no-cache')); + $this->assertEquals(200, $this->response->getStatusCode()); + $this->assertEquals('Hello World', $this->response->getContent()); + $this->assertTraceNotContains('reload'); + } + + public function testRevalidatesFreshCacheEntryWhenMaxAgeRequestDirectiveIsExceededWhenAllowRevalidateOptionIsSetTrue() + { + $count = 0; + + $this->setNextResponse(200, array(), '', function ($request, $response) use (&$count) { + ++$count; + $response->headers->set('Cache-Control', 'public, max-age=10000'); + $response->setETag($count); + $response->setContent(1 == $count ? 'Hello World' : 'Goodbye World'); + }); + + $this->request('GET', '/'); + $this->assertEquals(200, $this->response->getStatusCode()); + $this->assertEquals('Hello World', $this->response->getContent()); + $this->assertTraceContains('store'); + + $this->request('GET', '/'); + $this->assertEquals(200, $this->response->getStatusCode()); + $this->assertEquals('Hello World', $this->response->getContent()); + $this->assertTraceContains('fresh'); + + $this->cacheConfig['allow_revalidate'] = true; + $this->request('GET', '/', array('HTTP_CACHE_CONTROL' => 'max-age=0')); + $this->assertEquals(200, $this->response->getStatusCode()); + $this->assertEquals('Goodbye World', $this->response->getContent()); + $this->assertTraceContains('stale'); + $this->assertTraceContains('invalid'); + $this->assertTraceContains('store'); + } + + public function testDoesNotRevalidateFreshCacheEntryWhenEnableRevalidateOptionIsSetFalseDefault() + { + $count = 0; + + $this->setNextResponse(200, array(), '', function ($request, $response) use (&$count) { + ++$count; + $response->headers->set('Cache-Control', 'public, max-age=10000'); + $response->setETag($count); + $response->setContent(1 == $count ? 'Hello World' : 'Goodbye World'); + }); + + $this->request('GET', '/'); + $this->assertEquals(200, $this->response->getStatusCode()); + $this->assertEquals('Hello World', $this->response->getContent()); + $this->assertTraceContains('store'); + + $this->request('GET', '/'); + $this->assertEquals(200, $this->response->getStatusCode()); + $this->assertEquals('Hello World', $this->response->getContent()); + $this->assertTraceContains('fresh'); + + $this->cacheConfig['allow_revalidate'] = false; + $this->request('GET', '/', array('HTTP_CACHE_CONTROL' => 'max-age=0')); + $this->assertEquals(200, $this->response->getStatusCode()); + $this->assertEquals('Hello World', $this->response->getContent()); + $this->assertTraceNotContains('stale'); + $this->assertTraceNotContains('invalid'); + $this->assertTraceContains('fresh'); + + $this->request('GET', '/', array('HTTP_CACHE_CONTROL' => 'max-age=0')); + $this->assertEquals(200, $this->response->getStatusCode()); + $this->assertEquals('Hello World', $this->response->getContent()); + $this->assertTraceNotContains('stale'); + $this->assertTraceNotContains('invalid'); + $this->assertTraceContains('fresh'); + } + + public function testFetchesResponseFromBackendWhenCacheMisses() + { + $time = \DateTime::createFromFormat('U', time() + 5); + $this->setNextResponse(200, array('Cache-Control' => 'public', 'Expires' => $time->format(DATE_RFC2822))); + + $this->request('GET', '/'); + $this->assertEquals(200, $this->response->getStatusCode()); + $this->assertTraceContains('miss'); + $this->assertTrue($this->response->headers->has('Age')); + } + + public function testDoesNotCacheSomeStatusCodeResponses() + { + foreach (array_merge(range(201, 202), range(204, 206), range(303, 305), range(400, 403), range(405, 409), range(411, 417), range(500, 505)) as $code) { + $time = \DateTime::createFromFormat('U', time() + 5); + $this->setNextResponse($code, array('Expires' => $time->format(DATE_RFC2822))); + + $this->request('GET', '/'); + $this->assertEquals($code, $this->response->getStatusCode()); + $this->assertTraceNotContains('store'); + $this->assertFalse($this->response->headers->has('Age')); + } + } + + public function testDoesNotCacheResponsesWithExplicitNoStoreDirective() + { + $time = \DateTime::createFromFormat('U', time() + 5); + $this->setNextResponse(200, array('Expires' => $time->format(DATE_RFC2822), 'Cache-Control' => 'no-store')); + + $this->request('GET', '/'); + $this->assertTraceNotContains('store'); + $this->assertFalse($this->response->headers->has('Age')); + } + + public function testDoesNotCacheResponsesWithoutFreshnessInformationOrAValidator() + { + $this->setNextResponse(); + + $this->request('GET', '/'); + $this->assertEquals(200, $this->response->getStatusCode()); + $this->assertTraceNotContains('store'); + } + + public function testCachesResponsesWithExplicitNoCacheDirective() + { + $time = \DateTime::createFromFormat('U', time() + 5); + $this->setNextResponse(200, array('Expires' => $time->format(DATE_RFC2822), 'Cache-Control' => 'public, no-cache')); + + $this->request('GET', '/'); + $this->assertTraceContains('store'); + $this->assertTrue($this->response->headers->has('Age')); + } + + public function testCachesResponsesWithAnExpirationHeader() + { + $time = \DateTime::createFromFormat('U', time() + 5); + $this->setNextResponse(200, array('Cache-Control' => 'public', 'Expires' => $time->format(DATE_RFC2822))); + + $this->request('GET', '/'); + $this->assertEquals(200, $this->response->getStatusCode()); + $this->assertEquals('Hello World', $this->response->getContent()); + $this->assertNotNull($this->response->headers->get('Date')); + $this->assertNotNull($this->response->headers->get('X-Content-Digest')); + $this->assertTraceContains('miss'); + $this->assertTraceContains('store'); + + $values = $this->getMetaStorageValues(); + $this->assertCount(1, $values); + } + + public function testCachesResponsesWithAMaxAgeDirective() + { + $this->setNextResponse(200, array('Cache-Control' => 'public, max-age=5')); + + $this->request('GET', '/'); + $this->assertEquals(200, $this->response->getStatusCode()); + $this->assertEquals('Hello World', $this->response->getContent()); + $this->assertNotNull($this->response->headers->get('Date')); + $this->assertNotNull($this->response->headers->get('X-Content-Digest')); + $this->assertTraceContains('miss'); + $this->assertTraceContains('store'); + + $values = $this->getMetaStorageValues(); + $this->assertCount(1, $values); + } + + public function testCachesResponsesWithASMaxAgeDirective() + { + $this->setNextResponse(200, array('Cache-Control' => 's-maxage=5')); + + $this->request('GET', '/'); + $this->assertEquals(200, $this->response->getStatusCode()); + $this->assertEquals('Hello World', $this->response->getContent()); + $this->assertNotNull($this->response->headers->get('Date')); + $this->assertNotNull($this->response->headers->get('X-Content-Digest')); + $this->assertTraceContains('miss'); + $this->assertTraceContains('store'); + + $values = $this->getMetaStorageValues(); + $this->assertCount(1, $values); + } + + public function testCachesResponsesWithALastModifiedValidatorButNoFreshnessInformation() + { + $time = \DateTime::createFromFormat('U', time()); + $this->setNextResponse(200, array('Cache-Control' => 'public', 'Last-Modified' => $time->format(DATE_RFC2822))); + + $this->request('GET', '/'); + $this->assertEquals(200, $this->response->getStatusCode()); + $this->assertEquals('Hello World', $this->response->getContent()); + $this->assertTraceContains('miss'); + $this->assertTraceContains('store'); + } + + public function testCachesResponsesWithAnETagValidatorButNoFreshnessInformation() + { + $this->setNextResponse(200, array('Cache-Control' => 'public', 'ETag' => '"123456"')); + + $this->request('GET', '/'); + $this->assertEquals(200, $this->response->getStatusCode()); + $this->assertEquals('Hello World', $this->response->getContent()); + $this->assertTraceContains('miss'); + $this->assertTraceContains('store'); + } + + public function testHitsCachedResponsesWithExpiresHeader() + { + $time1 = \DateTime::createFromFormat('U', time() - 5); + $time2 = \DateTime::createFromFormat('U', time() + 5); + $this->setNextResponse(200, array('Cache-Control' => 'public', 'Date' => $time1->format(DATE_RFC2822), 'Expires' => $time2->format(DATE_RFC2822))); + + $this->request('GET', '/'); + $this->assertHttpKernelIsCalled(); + $this->assertEquals(200, $this->response->getStatusCode()); + $this->assertNotNull($this->response->headers->get('Date')); + $this->assertTraceContains('miss'); + $this->assertTraceContains('store'); + $this->assertEquals('Hello World', $this->response->getContent()); + + $this->request('GET', '/'); + $this->assertHttpKernelIsNotCalled(); + $this->assertEquals(200, $this->response->getStatusCode()); + $this->assertLessThan(2, strtotime($this->responses[0]->headers->get('Date')) - strtotime($this->response->headers->get('Date'))); + $this->assertGreaterThan(0, $this->response->headers->get('Age')); + $this->assertNotNull($this->response->headers->get('X-Content-Digest')); + $this->assertTraceContains('fresh'); + $this->assertTraceNotContains('store'); + $this->assertEquals('Hello World', $this->response->getContent()); + } + + public function testHitsCachedResponseWithMaxAgeDirective() + { + $time = \DateTime::createFromFormat('U', time() - 5); + $this->setNextResponse(200, array('Date' => $time->format(DATE_RFC2822), 'Cache-Control' => 'public, max-age=10')); + + $this->request('GET', '/'); + $this->assertHttpKernelIsCalled(); + $this->assertEquals(200, $this->response->getStatusCode()); + $this->assertNotNull($this->response->headers->get('Date')); + $this->assertTraceContains('miss'); + $this->assertTraceContains('store'); + $this->assertEquals('Hello World', $this->response->getContent()); + + $this->request('GET', '/'); + $this->assertHttpKernelIsNotCalled(); + $this->assertEquals(200, $this->response->getStatusCode()); + $this->assertLessThan(2, strtotime($this->responses[0]->headers->get('Date')) - strtotime($this->response->headers->get('Date'))); + $this->assertGreaterThan(0, $this->response->headers->get('Age')); + $this->assertNotNull($this->response->headers->get('X-Content-Digest')); + $this->assertTraceContains('fresh'); + $this->assertTraceNotContains('store'); + $this->assertEquals('Hello World', $this->response->getContent()); + } + + public function testDegradationWhenCacheLocked() + { + if ('\\' === DIRECTORY_SEPARATOR) { + $this->markTestSkipped('Skips on windows to avoid permissions issues.'); + } + + $this->cacheConfig['stale_while_revalidate'] = 10; + + // The prescence of Last-Modified makes this cacheable (because Response::isValidateable() then). + $this->setNextResponse(200, array('Cache-Control' => 'public, s-maxage=5', 'Last-Modified' => 'some while ago'), 'Old response'); + $this->request('GET', '/'); // warm the cache + + // Now, lock the cache + $concurrentRequest = Request::create('/', 'GET'); + $this->store->lock($concurrentRequest); + + /* + * After 10s, the cached response has become stale. Yet, we're still within the "stale_while_revalidate" + * timeout so we may serve the stale response. + */ + sleep(10); + + $this->request('GET', '/'); + $this->assertHttpKernelIsNotCalled(); + $this->assertEquals(200, $this->response->getStatusCode()); + $this->assertTraceContains('stale-while-revalidate'); + $this->assertEquals('Old response', $this->response->getContent()); + + /* + * Another 10s later, stale_while_revalidate is over. Resort to serving the old response, but + * do so with a "server unavailable" message. + */ + sleep(10); + + $this->request('GET', '/'); + $this->assertHttpKernelIsNotCalled(); + $this->assertEquals(503, $this->response->getStatusCode()); + $this->assertEquals('Old response', $this->response->getContent()); + } + + public function testHitsCachedResponseWithSMaxAgeDirective() + { + $time = \DateTime::createFromFormat('U', time() - 5); + $this->setNextResponse(200, array('Date' => $time->format(DATE_RFC2822), 'Cache-Control' => 's-maxage=10, max-age=0')); + + $this->request('GET', '/'); + $this->assertHttpKernelIsCalled(); + $this->assertEquals(200, $this->response->getStatusCode()); + $this->assertNotNull($this->response->headers->get('Date')); + $this->assertTraceContains('miss'); + $this->assertTraceContains('store'); + $this->assertEquals('Hello World', $this->response->getContent()); + + $this->request('GET', '/'); + $this->assertHttpKernelIsNotCalled(); + $this->assertEquals(200, $this->response->getStatusCode()); + $this->assertLessThan(2, strtotime($this->responses[0]->headers->get('Date')) - strtotime($this->response->headers->get('Date'))); + $this->assertGreaterThan(0, $this->response->headers->get('Age')); + $this->assertNotNull($this->response->headers->get('X-Content-Digest')); + $this->assertTraceContains('fresh'); + $this->assertTraceNotContains('store'); + $this->assertEquals('Hello World', $this->response->getContent()); + } + + public function testAssignsDefaultTtlWhenResponseHasNoFreshnessInformation() + { + $this->setNextResponse(); + + $this->cacheConfig['default_ttl'] = 10; + $this->request('GET', '/'); + $this->assertHttpKernelIsCalled(); + $this->assertTraceContains('miss'); + $this->assertTraceContains('store'); + $this->assertEquals('Hello World', $this->response->getContent()); + $this->assertRegExp('/s-maxage=10/', $this->response->headers->get('Cache-Control')); + + $this->cacheConfig['default_ttl'] = 10; + $this->request('GET', '/'); + $this->assertHttpKernelIsNotCalled(); + $this->assertEquals(200, $this->response->getStatusCode()); + $this->assertTraceContains('fresh'); + $this->assertTraceNotContains('store'); + $this->assertEquals('Hello World', $this->response->getContent()); + $this->assertRegExp('/s-maxage=10/', $this->response->headers->get('Cache-Control')); + } + + public function testAssignsDefaultTtlWhenResponseHasNoFreshnessInformationAndAfterTtlWasExpired() + { + $this->setNextResponse(); + + $this->cacheConfig['default_ttl'] = 2; + $this->request('GET', '/'); + $this->assertHttpKernelIsCalled(); + $this->assertTraceContains('miss'); + $this->assertTraceContains('store'); + $this->assertEquals('Hello World', $this->response->getContent()); + $this->assertRegExp('/s-maxage=2/', $this->response->headers->get('Cache-Control')); + + $this->request('GET', '/'); + $this->assertHttpKernelIsNotCalled(); + $this->assertEquals(200, $this->response->getStatusCode()); + $this->assertTraceContains('fresh'); + $this->assertTraceNotContains('store'); + $this->assertEquals('Hello World', $this->response->getContent()); + $this->assertRegExp('/s-maxage=2/', $this->response->headers->get('Cache-Control')); + + // expires the cache + $values = $this->getMetaStorageValues(); + $this->assertCount(1, $values); + $tmp = unserialize($values[0]); + $time = \DateTime::createFromFormat('U', time() - 5); + $tmp[0][1]['date'] = $time->format(DATE_RFC2822); + $r = new \ReflectionObject($this->store); + $m = $r->getMethod('save'); + $m->setAccessible(true); + $m->invoke($this->store, 'md'.hash('sha256', 'http://localhost/'), serialize($tmp)); + + $this->request('GET', '/'); + $this->assertHttpKernelIsCalled(); + $this->assertEquals(200, $this->response->getStatusCode()); + $this->assertTraceContains('stale'); + $this->assertTraceContains('invalid'); + $this->assertTraceContains('store'); + $this->assertEquals('Hello World', $this->response->getContent()); + $this->assertRegExp('/s-maxage=2/', $this->response->headers->get('Cache-Control')); + + $this->setNextResponse(); + + $this->request('GET', '/'); + $this->assertHttpKernelIsNotCalled(); + $this->assertEquals(200, $this->response->getStatusCode()); + $this->assertTraceContains('fresh'); + $this->assertTraceNotContains('store'); + $this->assertEquals('Hello World', $this->response->getContent()); + $this->assertRegExp('/s-maxage=2/', $this->response->headers->get('Cache-Control')); + } + + public function testAssignsDefaultTtlWhenResponseHasNoFreshnessInformationAndAfterTtlWasExpiredWithStatus304() + { + $this->setNextResponse(); + + $this->cacheConfig['default_ttl'] = 2; + $this->request('GET', '/'); + $this->assertHttpKernelIsCalled(); + $this->assertTraceContains('miss'); + $this->assertTraceContains('store'); + $this->assertEquals('Hello World', $this->response->getContent()); + $this->assertRegExp('/s-maxage=2/', $this->response->headers->get('Cache-Control')); + + $this->request('GET', '/'); + $this->assertHttpKernelIsNotCalled(); + $this->assertEquals(200, $this->response->getStatusCode()); + $this->assertTraceContains('fresh'); + $this->assertTraceNotContains('store'); + $this->assertEquals('Hello World', $this->response->getContent()); + + // expires the cache + $values = $this->getMetaStorageValues(); + $this->assertCount(1, $values); + $tmp = unserialize($values[0]); + $time = \DateTime::createFromFormat('U', time() - 5); + $tmp[0][1]['date'] = $time->format(DATE_RFC2822); + $r = new \ReflectionObject($this->store); + $m = $r->getMethod('save'); + $m->setAccessible(true); + $m->invoke($this->store, 'md'.hash('sha256', 'http://localhost/'), serialize($tmp)); + + $this->request('GET', '/'); + $this->assertHttpKernelIsCalled(); + $this->assertEquals(200, $this->response->getStatusCode()); + $this->assertTraceContains('stale'); + $this->assertTraceContains('valid'); + $this->assertTraceContains('store'); + $this->assertTraceNotContains('miss'); + $this->assertEquals('Hello World', $this->response->getContent()); + $this->assertRegExp('/s-maxage=2/', $this->response->headers->get('Cache-Control')); + + $this->request('GET', '/'); + $this->assertHttpKernelIsNotCalled(); + $this->assertEquals(200, $this->response->getStatusCode()); + $this->assertTraceContains('fresh'); + $this->assertTraceNotContains('store'); + $this->assertEquals('Hello World', $this->response->getContent()); + $this->assertRegExp('/s-maxage=2/', $this->response->headers->get('Cache-Control')); + } + + public function testDoesNotAssignDefaultTtlWhenResponseHasMustRevalidateDirective() + { + $this->setNextResponse(200, array('Cache-Control' => 'must-revalidate')); + + $this->cacheConfig['default_ttl'] = 10; + $this->request('GET', '/'); + $this->assertHttpKernelIsCalled(); + $this->assertEquals(200, $this->response->getStatusCode()); + $this->assertTraceContains('miss'); + $this->assertTraceNotContains('store'); + $this->assertNotRegExp('/s-maxage/', $this->response->headers->get('Cache-Control')); + $this->assertEquals('Hello World', $this->response->getContent()); + } + + public function testFetchesFullResponseWhenCacheStaleAndNoValidatorsPresent() + { + $time = \DateTime::createFromFormat('U', time() + 5); + $this->setNextResponse(200, array('Cache-Control' => 'public', 'Expires' => $time->format(DATE_RFC2822))); + + // build initial request + $this->request('GET', '/'); + $this->assertHttpKernelIsCalled(); + $this->assertEquals(200, $this->response->getStatusCode()); + $this->assertNotNull($this->response->headers->get('Date')); + $this->assertNotNull($this->response->headers->get('X-Content-Digest')); + $this->assertNotNull($this->response->headers->get('Age')); + $this->assertTraceContains('miss'); + $this->assertTraceContains('store'); + $this->assertEquals('Hello World', $this->response->getContent()); + + // go in and play around with the cached metadata directly ... + $values = $this->getMetaStorageValues(); + $this->assertCount(1, $values); + $tmp = unserialize($values[0]); + $time = \DateTime::createFromFormat('U', time()); + $tmp[0][1]['expires'] = $time->format(DATE_RFC2822); + $r = new \ReflectionObject($this->store); + $m = $r->getMethod('save'); + $m->setAccessible(true); + $m->invoke($this->store, 'md'.hash('sha256', 'http://localhost/'), serialize($tmp)); + + // build subsequent request; should be found but miss due to freshness + $this->request('GET', '/'); + $this->assertHttpKernelIsCalled(); + $this->assertEquals(200, $this->response->getStatusCode()); + $this->assertLessThanOrEqual(1, $this->response->headers->get('Age')); + $this->assertNotNull($this->response->headers->get('X-Content-Digest')); + $this->assertTraceContains('stale'); + $this->assertTraceNotContains('fresh'); + $this->assertTraceNotContains('miss'); + $this->assertTraceContains('store'); + $this->assertEquals('Hello World', $this->response->getContent()); + } + + public function testValidatesCachedResponsesWithLastModifiedAndNoFreshnessInformation() + { + $time = \DateTime::createFromFormat('U', time()); + $this->setNextResponse(200, array(), 'Hello World', function ($request, $response) use ($time) { + $response->headers->set('Cache-Control', 'public'); + $response->headers->set('Last-Modified', $time->format(DATE_RFC2822)); + if ($time->format(DATE_RFC2822) == $request->headers->get('IF_MODIFIED_SINCE')) { + $response->setStatusCode(304); + $response->setContent(''); + } + }); + + // build initial request + $this->request('GET', '/'); + $this->assertHttpKernelIsCalled(); + $this->assertEquals(200, $this->response->getStatusCode()); + $this->assertNotNull($this->response->headers->get('Last-Modified')); + $this->assertNotNull($this->response->headers->get('X-Content-Digest')); + $this->assertEquals('Hello World', $this->response->getContent()); + $this->assertTraceContains('miss'); + $this->assertTraceContains('store'); + $this->assertTraceNotContains('stale'); + + // build subsequent request; should be found but miss due to freshness + $this->request('GET', '/'); + $this->assertHttpKernelIsCalled(); + $this->assertEquals(200, $this->response->getStatusCode()); + $this->assertNotNull($this->response->headers->get('Last-Modified')); + $this->assertNotNull($this->response->headers->get('X-Content-Digest')); + $this->assertLessThanOrEqual(1, $this->response->headers->get('Age')); + $this->assertEquals('Hello World', $this->response->getContent()); + $this->assertTraceContains('stale'); + $this->assertTraceContains('valid'); + $this->assertTraceContains('store'); + $this->assertTraceNotContains('miss'); + } + + public function testValidatesCachedResponsesUseSameHttpMethod() + { + $test = $this; + + $this->setNextResponse(200, array(), 'Hello World', function ($request, $response) use ($test) { + $test->assertSame('OPTIONS', $request->getMethod()); + }); + + // build initial request + $this->request('OPTIONS', '/'); + + // build subsequent request + $this->request('OPTIONS', '/'); + } + + public function testValidatesCachedResponsesWithETagAndNoFreshnessInformation() + { + $this->setNextResponse(200, array(), 'Hello World', function ($request, $response) { + $response->headers->set('Cache-Control', 'public'); + $response->headers->set('ETag', '"12345"'); + if ($response->getETag() == $request->headers->get('IF_NONE_MATCH')) { + $response->setStatusCode(304); + $response->setContent(''); + } + }); + + // build initial request + $this->request('GET', '/'); + $this->assertHttpKernelIsCalled(); + $this->assertEquals(200, $this->response->getStatusCode()); + $this->assertNotNull($this->response->headers->get('ETag')); + $this->assertNotNull($this->response->headers->get('X-Content-Digest')); + $this->assertEquals('Hello World', $this->response->getContent()); + $this->assertTraceContains('miss'); + $this->assertTraceContains('store'); + + // build subsequent request; should be found but miss due to freshness + $this->request('GET', '/'); + $this->assertHttpKernelIsCalled(); + $this->assertEquals(200, $this->response->getStatusCode()); + $this->assertNotNull($this->response->headers->get('ETag')); + $this->assertNotNull($this->response->headers->get('X-Content-Digest')); + $this->assertLessThanOrEqual(1, $this->response->headers->get('Age')); + $this->assertEquals('Hello World', $this->response->getContent()); + $this->assertTraceContains('stale'); + $this->assertTraceContains('valid'); + $this->assertTraceContains('store'); + $this->assertTraceNotContains('miss'); + } + + public function testServesResponseWhileFreshAndRevalidatesWithLastModifiedInformation() + { + $time = \DateTime::createFromFormat('U', time()); + + $this->setNextResponse(200, array(), 'Hello World', function (Request $request, Response $response) use ($time) { + $response->setSharedMaxAge(10); + $response->headers->set('Last-Modified', $time->format(DATE_RFC2822)); + }); + + // prime the cache + $this->request('GET', '/'); + + // next request before s-maxage has expired: Serve from cache + // without hitting the backend + $this->request('GET', '/'); + $this->assertHttpKernelIsNotCalled(); + $this->assertEquals(200, $this->response->getStatusCode()); + $this->assertEquals('Hello World', $this->response->getContent()); + $this->assertTraceContains('fresh'); + + sleep(15); // expire the cache + + $this->setNextResponse(304, array(), '', function (Request $request, Response $response) use ($time) { + $this->assertEquals($time->format(DATE_RFC2822), $request->headers->get('IF_MODIFIED_SINCE')); + }); + + $this->request('GET', '/'); + $this->assertHttpKernelIsCalled(); + $this->assertEquals(200, $this->response->getStatusCode()); + $this->assertEquals('Hello World', $this->response->getContent()); + $this->assertTraceContains('stale'); + $this->assertTraceContains('valid'); + } + + public function testReplacesCachedResponsesWhenValidationResultsInNon304Response() + { + $time = \DateTime::createFromFormat('U', time()); + $count = 0; + $this->setNextResponse(200, array(), 'Hello World', function ($request, $response) use ($time, &$count) { + $response->headers->set('Last-Modified', $time->format(DATE_RFC2822)); + $response->headers->set('Cache-Control', 'public'); + switch (++$count) { + case 1: + $response->setContent('first response'); + break; + case 2: + $response->setContent('second response'); + break; + case 3: + $response->setContent(''); + $response->setStatusCode(304); + break; + } + }); + + // first request should fetch from backend and store in cache + $this->request('GET', '/'); + $this->assertEquals(200, $this->response->getStatusCode()); + $this->assertEquals('first response', $this->response->getContent()); + + // second request is validated, is invalid, and replaces cached entry + $this->request('GET', '/'); + $this->assertEquals(200, $this->response->getStatusCode()); + $this->assertEquals('second response', $this->response->getContent()); + + // third response is validated, valid, and returns cached entry + $this->request('GET', '/'); + $this->assertEquals(200, $this->response->getStatusCode()); + $this->assertEquals('second response', $this->response->getContent()); + + $this->assertEquals(3, $count); + } + + public function testPassesHeadRequestsThroughDirectlyOnPass() + { + $this->setNextResponse(200, array(), 'Hello World', function ($request, $response) { + $response->setContent(''); + $response->setStatusCode(200); + $this->assertEquals('HEAD', $request->getMethod()); + }); + + $this->request('HEAD', '/', array('HTTP_EXPECT' => 'something ...')); + $this->assertHttpKernelIsCalled(); + $this->assertEquals('', $this->response->getContent()); + } + + public function testUsesCacheToRespondToHeadRequestsWhenFresh() + { + $this->setNextResponse(200, array(), 'Hello World', function ($request, $response) { + $response->headers->set('Cache-Control', 'public, max-age=10'); + $response->setContent('Hello World'); + $response->setStatusCode(200); + $this->assertNotEquals('HEAD', $request->getMethod()); + }); + + $this->request('GET', '/'); + $this->assertHttpKernelIsCalled(); + $this->assertEquals('Hello World', $this->response->getContent()); + + $this->request('HEAD', '/'); + $this->assertHttpKernelIsNotCalled(); + $this->assertEquals(200, $this->response->getStatusCode()); + $this->assertEquals('', $this->response->getContent()); + $this->assertEquals(strlen('Hello World'), $this->response->headers->get('Content-Length')); + } + + public function testSendsNoContentWhenFresh() + { + $time = \DateTime::createFromFormat('U', time()); + $this->setNextResponse(200, array(), 'Hello World', function ($request, $response) use ($time) { + $response->headers->set('Cache-Control', 'public, max-age=10'); + $response->headers->set('Last-Modified', $time->format(DATE_RFC2822)); + }); + + $this->request('GET', '/'); + $this->assertHttpKernelIsCalled(); + $this->assertEquals('Hello World', $this->response->getContent()); + + $this->request('GET', '/', array('HTTP_IF_MODIFIED_SINCE' => $time->format(DATE_RFC2822))); + $this->assertHttpKernelIsNotCalled(); + $this->assertEquals(304, $this->response->getStatusCode()); + $this->assertEquals('', $this->response->getContent()); + } + + public function testInvalidatesCachedResponsesOnPost() + { + $this->setNextResponse(200, array(), 'Hello World', function ($request, $response) { + if ('GET' == $request->getMethod()) { + $response->setStatusCode(200); + $response->headers->set('Cache-Control', 'public, max-age=500'); + $response->setContent('Hello World'); + } elseif ('POST' == $request->getMethod()) { + $response->setStatusCode(303); + $response->headers->set('Location', '/'); + $response->headers->remove('Cache-Control'); + $response->setContent(''); + } + }); + + // build initial request to enter into the cache + $this->request('GET', '/'); + $this->assertHttpKernelIsCalled(); + $this->assertEquals(200, $this->response->getStatusCode()); + $this->assertEquals('Hello World', $this->response->getContent()); + $this->assertTraceContains('miss'); + $this->assertTraceContains('store'); + + // make sure it is valid + $this->request('GET', '/'); + $this->assertHttpKernelIsNotCalled(); + $this->assertEquals(200, $this->response->getStatusCode()); + $this->assertEquals('Hello World', $this->response->getContent()); + $this->assertTraceContains('fresh'); + + // now POST to same URL + $this->request('POST', '/helloworld'); + $this->assertHttpKernelIsCalled(); + $this->assertEquals('/', $this->response->headers->get('Location')); + $this->assertTraceContains('invalidate'); + $this->assertTraceContains('pass'); + $this->assertEquals('', $this->response->getContent()); + + // now make sure it was actually invalidated + $this->request('GET', '/'); + $this->assertHttpKernelIsCalled(); + $this->assertEquals(200, $this->response->getStatusCode()); + $this->assertEquals('Hello World', $this->response->getContent()); + $this->assertTraceContains('stale'); + $this->assertTraceContains('invalid'); + $this->assertTraceContains('store'); + } + + public function testServesFromCacheWhenHeadersMatch() + { + $count = 0; + $this->setNextResponse(200, array('Cache-Control' => 'max-age=10000'), '', function ($request, $response) use (&$count) { + $response->headers->set('Vary', 'Accept User-Agent Foo'); + $response->headers->set('Cache-Control', 'public, max-age=10'); + $response->headers->set('X-Response-Count', ++$count); + $response->setContent($request->headers->get('USER_AGENT')); + }); + + $this->request('GET', '/', array('HTTP_ACCEPT' => 'text/html', 'HTTP_USER_AGENT' => 'Bob/1.0')); + $this->assertEquals(200, $this->response->getStatusCode()); + $this->assertEquals('Bob/1.0', $this->response->getContent()); + $this->assertTraceContains('miss'); + $this->assertTraceContains('store'); + + $this->request('GET', '/', array('HTTP_ACCEPT' => 'text/html', 'HTTP_USER_AGENT' => 'Bob/1.0')); + $this->assertEquals(200, $this->response->getStatusCode()); + $this->assertEquals('Bob/1.0', $this->response->getContent()); + $this->assertTraceContains('fresh'); + $this->assertTraceNotContains('store'); + $this->assertNotNull($this->response->headers->get('X-Content-Digest')); + } + + public function testStoresMultipleResponsesWhenHeadersDiffer() + { + $count = 0; + $this->setNextResponse(200, array('Cache-Control' => 'max-age=10000'), '', function ($request, $response) use (&$count) { + $response->headers->set('Vary', 'Accept User-Agent Foo'); + $response->headers->set('Cache-Control', 'public, max-age=10'); + $response->headers->set('X-Response-Count', ++$count); + $response->setContent($request->headers->get('USER_AGENT')); + }); + + $this->request('GET', '/', array('HTTP_ACCEPT' => 'text/html', 'HTTP_USER_AGENT' => 'Bob/1.0')); + $this->assertEquals(200, $this->response->getStatusCode()); + $this->assertEquals('Bob/1.0', $this->response->getContent()); + $this->assertEquals(1, $this->response->headers->get('X-Response-Count')); + + $this->request('GET', '/', array('HTTP_ACCEPT' => 'text/html', 'HTTP_USER_AGENT' => 'Bob/2.0')); + $this->assertEquals(200, $this->response->getStatusCode()); + $this->assertTraceContains('miss'); + $this->assertTraceContains('store'); + $this->assertEquals('Bob/2.0', $this->response->getContent()); + $this->assertEquals(2, $this->response->headers->get('X-Response-Count')); + + $this->request('GET', '/', array('HTTP_ACCEPT' => 'text/html', 'HTTP_USER_AGENT' => 'Bob/1.0')); + $this->assertTraceContains('fresh'); + $this->assertEquals('Bob/1.0', $this->response->getContent()); + $this->assertEquals(1, $this->response->headers->get('X-Response-Count')); + + $this->request('GET', '/', array('HTTP_ACCEPT' => 'text/html', 'HTTP_USER_AGENT' => 'Bob/2.0')); + $this->assertTraceContains('fresh'); + $this->assertEquals('Bob/2.0', $this->response->getContent()); + $this->assertEquals(2, $this->response->headers->get('X-Response-Count')); + + $this->request('GET', '/', array('HTTP_USER_AGENT' => 'Bob/2.0')); + $this->assertTraceContains('miss'); + $this->assertEquals('Bob/2.0', $this->response->getContent()); + $this->assertEquals(3, $this->response->headers->get('X-Response-Count')); + } + + public function testShouldCatchExceptions() + { + $this->catchExceptions(); + + $this->setNextResponse(); + $this->request('GET', '/'); + + $this->assertExceptionsAreCaught(); + } + + public function testShouldCatchExceptionsWhenReloadingAndNoCacheRequest() + { + $this->catchExceptions(); + + $this->setNextResponse(); + $this->cacheConfig['allow_reload'] = true; + $this->request('GET', '/', array(), array(), false, array('Pragma' => 'no-cache')); + + $this->assertExceptionsAreCaught(); + } + + public function testShouldNotCatchExceptions() + { + $this->catchExceptions(false); + + $this->setNextResponse(); + $this->request('GET', '/'); + + $this->assertExceptionsAreNotCaught(); + } + + public function testEsiCacheSendsTheLowestTtl() + { + $responses = array( + array( + 'status' => 200, + 'body' => ' ', + 'headers' => array( + 'Cache-Control' => 's-maxage=300', + 'Surrogate-Control' => 'content="ESI/1.0"', + ), + ), + array( + 'status' => 200, + 'body' => 'Hello World!', + 'headers' => array('Cache-Control' => 's-maxage=200'), + ), + array( + 'status' => 200, + 'body' => 'My name is Bobby.', + 'headers' => array('Cache-Control' => 's-maxage=100'), + ), + ); + + $this->setNextResponses($responses); + + $this->request('GET', '/', array(), array(), true); + $this->assertEquals('Hello World! My name is Bobby.', $this->response->getContent()); + + $this->assertEquals(100, $this->response->getTtl()); + } + + public function testEsiCacheSendsTheLowestTtlForHeadRequests() + { + $responses = array( + array( + 'status' => 200, + 'body' => 'I am a long-lived master response, but I embed a short-lived resource: ', + 'headers' => array( + 'Cache-Control' => 's-maxage=300', + 'Surrogate-Control' => 'content="ESI/1.0"', + ), + ), + array( + 'status' => 200, + 'body' => 'I am a short-lived resource', + 'headers' => array('Cache-Control' => 's-maxage=100'), + ), + ); + + $this->setNextResponses($responses); + + $this->request('HEAD', '/', array(), array(), true); + + $this->assertEmpty($this->response->getContent()); + $this->assertEquals(100, $this->response->getTtl()); + } + + public function testEsiCacheForceValidation() + { + $responses = array( + array( + 'status' => 200, + 'body' => ' ', + 'headers' => array( + 'Cache-Control' => 's-maxage=300', + 'Surrogate-Control' => 'content="ESI/1.0"', + ), + ), + array( + 'status' => 200, + 'body' => 'Hello World!', + 'headers' => array('ETag' => 'foobar'), + ), + array( + 'status' => 200, + 'body' => 'My name is Bobby.', + 'headers' => array('Cache-Control' => 's-maxage=100'), + ), + ); + + $this->setNextResponses($responses); + + $this->request('GET', '/', array(), array(), true); + $this->assertEquals('Hello World! My name is Bobby.', $this->response->getContent()); + $this->assertNull($this->response->getTtl()); + $this->assertTrue($this->response->mustRevalidate()); + $this->assertTrue($this->response->headers->hasCacheControlDirective('private')); + $this->assertTrue($this->response->headers->hasCacheControlDirective('no-cache')); + } + + public function testEsiCacheForceValidationForHeadRequests() + { + $responses = array( + array( + 'status' => 200, + 'body' => 'I am the master response and use expiration caching, but I embed another resource: ', + 'headers' => array( + 'Cache-Control' => 's-maxage=300', + 'Surrogate-Control' => 'content="ESI/1.0"', + ), + ), + array( + 'status' => 200, + 'body' => 'I am the embedded resource and use validation caching', + 'headers' => array('ETag' => 'foobar'), + ), + ); + + $this->setNextResponses($responses); + + $this->request('HEAD', '/', array(), array(), true); + + // The response has been assembled from expiration and validation based resources + // This can neither be cached nor revalidated, so it should be private/no cache + $this->assertEmpty($this->response->getContent()); + $this->assertNull($this->response->getTtl()); + $this->assertTrue($this->response->mustRevalidate()); + $this->assertTrue($this->response->headers->hasCacheControlDirective('private')); + $this->assertTrue($this->response->headers->hasCacheControlDirective('no-cache')); + } + + public function testEsiRecalculateContentLengthHeader() + { + $responses = array( + array( + 'status' => 200, + 'body' => '', + 'headers' => array( + 'Content-Length' => 26, + 'Surrogate-Control' => 'content="ESI/1.0"', + ), + ), + array( + 'status' => 200, + 'body' => 'Hello World!', + 'headers' => array(), + ), + ); + + $this->setNextResponses($responses); + + $this->request('GET', '/', array(), array(), true); + $this->assertEquals('Hello World!', $this->response->getContent()); + $this->assertEquals(12, $this->response->headers->get('Content-Length')); + } + + public function testEsiRecalculateContentLengthHeaderForHeadRequest() + { + $responses = array( + array( + 'status' => 200, + 'body' => '', + 'headers' => array( + 'Content-Length' => 26, + 'Surrogate-Control' => 'content="ESI/1.0"', + ), + ), + array( + 'status' => 200, + 'body' => 'Hello World!', + 'headers' => array(), + ), + ); + + $this->setNextResponses($responses); + + $this->request('HEAD', '/', array(), array(), true); + + // https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.13 + // "The Content-Length entity-header field indicates the size of the entity-body, + // in decimal number of OCTETs, sent to the recipient or, in the case of the HEAD + // method, the size of the entity-body that would have been sent had the request + // been a GET." + $this->assertEmpty($this->response->getContent()); + $this->assertEquals(12, $this->response->headers->get('Content-Length')); + } + + public function testClientIpIsAlwaysLocalhostForForwardedRequests() + { + $this->setNextResponse(); + $this->request('GET', '/', array('REMOTE_ADDR' => '10.0.0.1')); + + $this->assertEquals('127.0.0.1', $this->kernel->getBackendRequest()->server->get('REMOTE_ADDR')); + } + + /** + * @dataProvider getTrustedProxyData + */ + public function testHttpCacheIsSetAsATrustedProxy(array $existing, array $expected) + { + Request::setTrustedProxies($existing, Request::HEADER_X_FORWARDED_ALL); + + $this->setNextResponse(); + $this->request('GET', '/', array('REMOTE_ADDR' => '10.0.0.1')); + + $this->assertEquals($expected, Request::getTrustedProxies()); + } + + public function getTrustedProxyData() + { + return array( + array(array(), array('127.0.0.1')), + array(array('10.0.0.2'), array('10.0.0.2', '127.0.0.1')), + array(array('10.0.0.2', '127.0.0.1'), array('10.0.0.2', '127.0.0.1')), + ); + } + + /** + * @dataProvider getXForwardedForData + */ + public function testXForwarderForHeaderForForwardedRequests($xForwardedFor, $expected) + { + $this->setNextResponse(); + $server = array('REMOTE_ADDR' => '10.0.0.1'); + if (false !== $xForwardedFor) { + $server['HTTP_X_FORWARDED_FOR'] = $xForwardedFor; + } + $this->request('GET', '/', $server); + + $this->assertEquals($expected, $this->kernel->getBackendRequest()->headers->get('X-Forwarded-For')); + } + + public function getXForwardedForData() + { + return array( + array(false, '10.0.0.1'), + array('10.0.0.2', '10.0.0.2, 10.0.0.1'), + array('10.0.0.2, 10.0.0.3', '10.0.0.2, 10.0.0.3, 10.0.0.1'), + ); + } + + public function testXForwarderForHeaderForPassRequests() + { + $this->setNextResponse(); + $server = array('REMOTE_ADDR' => '10.0.0.1'); + $this->request('POST', '/', $server); + + $this->assertEquals('10.0.0.1', $this->kernel->getBackendRequest()->headers->get('X-Forwarded-For')); + } + + public function testEsiCacheRemoveValidationHeadersIfEmbeddedResponses() + { + $time = \DateTime::createFromFormat('U', time()); + + $responses = array( + array( + 'status' => 200, + 'body' => '', + 'headers' => array( + 'Surrogate-Control' => 'content="ESI/1.0"', + 'ETag' => 'hey', + 'Last-Modified' => $time->format(DATE_RFC2822), + ), + ), + array( + 'status' => 200, + 'body' => 'Hey!', + 'headers' => array(), + ), + ); + + $this->setNextResponses($responses); + + $this->request('GET', '/', array(), array(), true); + $this->assertNull($this->response->getETag()); + $this->assertNull($this->response->getLastModified()); + } + + public function testEsiCacheRemoveValidationHeadersIfEmbeddedResponsesAndHeadRequest() + { + $time = \DateTime::createFromFormat('U', time()); + + $responses = array( + array( + 'status' => 200, + 'body' => '', + 'headers' => array( + 'Surrogate-Control' => 'content="ESI/1.0"', + 'ETag' => 'hey', + 'Last-Modified' => $time->format(DATE_RFC2822), + ), + ), + array( + 'status' => 200, + 'body' => 'Hey!', + 'headers' => array(), + ), + ); + + $this->setNextResponses($responses); + + $this->request('HEAD', '/', array(), array(), true); + $this->assertEmpty($this->response->getContent()); + $this->assertNull($this->response->getETag()); + $this->assertNull($this->response->getLastModified()); + } + + public function testDoesNotCacheOptionsRequest() + { + $this->setNextResponse(200, array('Cache-Control' => 'public, s-maxage=60'), 'get'); + $this->request('GET', '/'); + $this->assertHttpKernelIsCalled(); + + $this->setNextResponse(200, array('Cache-Control' => 'public, s-maxage=60'), 'options'); + $this->request('OPTIONS', '/'); + $this->assertHttpKernelIsCalled(); + + $this->request('GET', '/'); + $this->assertHttpKernelIsNotCalled(); + $this->assertSame('get', $this->response->getContent()); + } +} + +class TestKernel implements HttpKernelInterface +{ + public $terminateCalled = false; + + public function terminate(Request $request, Response $response) + { + $this->terminateCalled = true; + } + + public function handle(Request $request, $type = self::MASTER_REQUEST, $catch = true) + { + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/Tests/HttpCache/HttpCacheTestCase.php b/lib/silex/vendor/symfony/http-kernel/Tests/HttpCache/HttpCacheTestCase.php new file mode 100644 index 000000000..ed5c690d6 --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Tests/HttpCache/HttpCacheTestCase.php @@ -0,0 +1,185 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Tests\HttpCache; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpKernel\HttpCache\Esi; +use Symfony\Component\HttpKernel\HttpCache\HttpCache; +use Symfony\Component\HttpKernel\HttpCache\Store; +use Symfony\Component\HttpKernel\HttpKernelInterface; + +class HttpCacheTestCase extends TestCase +{ + protected $kernel; + protected $cache; + protected $caches; + protected $cacheConfig; + protected $request; + protected $response; + protected $responses; + protected $catch; + protected $esi; + + /** + * @var Store + */ + protected $store; + + protected function setUp() + { + $this->kernel = null; + + $this->cache = null; + $this->esi = null; + $this->caches = array(); + $this->cacheConfig = array(); + + $this->request = null; + $this->response = null; + $this->responses = array(); + + $this->catch = false; + + $this->clearDirectory(sys_get_temp_dir().'/http_cache'); + } + + protected function tearDown() + { + if ($this->cache) { + $this->cache->getStore()->cleanup(); + } + $this->kernel = null; + $this->cache = null; + $this->caches = null; + $this->request = null; + $this->response = null; + $this->responses = null; + $this->cacheConfig = null; + $this->catch = null; + $this->esi = null; + + $this->clearDirectory(sys_get_temp_dir().'/http_cache'); + } + + public function assertHttpKernelIsCalled() + { + $this->assertTrue($this->kernel->hasBeenCalled()); + } + + public function assertHttpKernelIsNotCalled() + { + $this->assertFalse($this->kernel->hasBeenCalled()); + } + + public function assertResponseOk() + { + $this->assertEquals(200, $this->response->getStatusCode()); + } + + public function assertTraceContains($trace) + { + $traces = $this->cache->getTraces(); + $traces = current($traces); + + $this->assertRegExp('/'.$trace.'/', implode(', ', $traces)); + } + + public function assertTraceNotContains($trace) + { + $traces = $this->cache->getTraces(); + $traces = current($traces); + + $this->assertNotRegExp('/'.$trace.'/', implode(', ', $traces)); + } + + public function assertExceptionsAreCaught() + { + $this->assertTrue($this->kernel->isCatchingExceptions()); + } + + public function assertExceptionsAreNotCaught() + { + $this->assertFalse($this->kernel->isCatchingExceptions()); + } + + public function request($method, $uri = '/', $server = array(), $cookies = array(), $esi = false, $headers = array()) + { + if (null === $this->kernel) { + throw new \LogicException('You must call setNextResponse() before calling request().'); + } + + $this->kernel->reset(); + + $this->store = new Store(sys_get_temp_dir().'/http_cache'); + + $this->cacheConfig['debug'] = true; + + $this->esi = $esi ? new Esi() : null; + $this->cache = new HttpCache($this->kernel, $this->store, $this->esi, $this->cacheConfig); + $this->request = Request::create($uri, $method, array(), $cookies, array(), $server); + $this->request->headers->add($headers); + + $this->response = $this->cache->handle($this->request, HttpKernelInterface::MASTER_REQUEST, $this->catch); + + $this->responses[] = $this->response; + } + + public function getMetaStorageValues() + { + $values = array(); + foreach (new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator(sys_get_temp_dir().'/http_cache/md', \RecursiveDirectoryIterator::SKIP_DOTS), \RecursiveIteratorIterator::LEAVES_ONLY) as $file) { + $values[] = file_get_contents($file); + } + + return $values; + } + + // A basic response with 200 status code and a tiny body. + public function setNextResponse($statusCode = 200, array $headers = array(), $body = 'Hello World', \Closure $customizer = null) + { + $this->kernel = new TestHttpKernel($body, $statusCode, $headers, $customizer); + } + + public function setNextResponses($responses) + { + $this->kernel = new TestMultipleHttpKernel($responses); + } + + public function catchExceptions($catch = true) + { + $this->catch = $catch; + } + + public static function clearDirectory($directory) + { + if (!is_dir($directory)) { + return; + } + + $fp = opendir($directory); + while (false !== $file = readdir($fp)) { + if (!in_array($file, array('.', '..'))) { + if (is_link($directory.'/'.$file)) { + unlink($directory.'/'.$file); + } elseif (is_dir($directory.'/'.$file)) { + self::clearDirectory($directory.'/'.$file); + rmdir($directory.'/'.$file); + } else { + unlink($directory.'/'.$file); + } + } + } + + closedir($fp); + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/Tests/HttpCache/ResponseCacheStrategyTest.php b/lib/silex/vendor/symfony/http-kernel/Tests/HttpCache/ResponseCacheStrategyTest.php new file mode 100644 index 000000000..5e4c32222 --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Tests/HttpCache/ResponseCacheStrategyTest.php @@ -0,0 +1,222 @@ + + * + * This code is partially based on the Rack-Cache library by Ryan Tomayko, + * which is released under the MIT license. + * (based on commit 02d2b48d75bcb63cf1c0c7149c077ad256542801) + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Tests\HttpCache; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\HttpCache\ResponseCacheStrategy; + +class ResponseCacheStrategyTest extends TestCase +{ + public function testMinimumSharedMaxAgeWins() + { + $cacheStrategy = new ResponseCacheStrategy(); + + $response1 = new Response(); + $response1->setSharedMaxAge(60); + $cacheStrategy->add($response1); + + $response2 = new Response(); + $response2->setSharedMaxAge(3600); + $cacheStrategy->add($response2); + + $response = new Response(); + $response->setSharedMaxAge(86400); + $cacheStrategy->update($response); + + $this->assertSame('60', $response->headers->getCacheControlDirective('s-maxage')); + } + + public function testSharedMaxAgeNotSetIfNotSetInAnyEmbeddedRequest() + { + $cacheStrategy = new ResponseCacheStrategy(); + + $response1 = new Response(); + $response1->setSharedMaxAge(60); + $cacheStrategy->add($response1); + + $response2 = new Response(); + $cacheStrategy->add($response2); + + $response = new Response(); + $response->setSharedMaxAge(86400); + $cacheStrategy->update($response); + + $this->assertFalse($response->headers->hasCacheControlDirective('s-maxage')); + } + + public function testSharedMaxAgeNotSetIfNotSetInMasterRequest() + { + $cacheStrategy = new ResponseCacheStrategy(); + + $response1 = new Response(); + $response1->setSharedMaxAge(60); + $cacheStrategy->add($response1); + + $response2 = new Response(); + $response2->setSharedMaxAge(3600); + $cacheStrategy->add($response2); + + $response = new Response(); + $cacheStrategy->update($response); + + $this->assertFalse($response->headers->hasCacheControlDirective('s-maxage')); + } + + public function testMasterResponseNotCacheableWhenEmbeddedResponseRequiresValidation() + { + $cacheStrategy = new ResponseCacheStrategy(); + + $embeddedResponse = new Response(); + $embeddedResponse->setLastModified(new \DateTime()); + $cacheStrategy->add($embeddedResponse); + + $masterResponse = new Response(); + $masterResponse->setSharedMaxAge(3600); + $cacheStrategy->update($masterResponse); + + $this->assertTrue($masterResponse->headers->hasCacheControlDirective('no-cache')); + $this->assertTrue($masterResponse->headers->hasCacheControlDirective('must-revalidate')); + $this->assertFalse($masterResponse->isFresh()); + } + + public function testValidationOnMasterResponseIsNotPossibleWhenItContainsEmbeddedResponses() + { + $cacheStrategy = new ResponseCacheStrategy(); + + // This master response uses the "validation" model + $masterResponse = new Response(); + $masterResponse->setLastModified(new \DateTime()); + $masterResponse->setEtag('foo'); + + // Embedded response uses "expiry" model + $embeddedResponse = new Response(); + $masterResponse->setSharedMaxAge(3600); + $cacheStrategy->add($embeddedResponse); + + $cacheStrategy->update($masterResponse); + + $this->assertFalse($masterResponse->isValidateable()); + $this->assertFalse($masterResponse->headers->has('Last-Modified')); + $this->assertFalse($masterResponse->headers->has('ETag')); + $this->assertTrue($masterResponse->headers->hasCacheControlDirective('no-cache')); + $this->assertTrue($masterResponse->headers->hasCacheControlDirective('must-revalidate')); + } + + public function testMasterResponseWithValidationIsUnchangedWhenThereIsNoEmbeddedResponse() + { + $cacheStrategy = new ResponseCacheStrategy(); + + $masterResponse = new Response(); + $masterResponse->setLastModified(new \DateTime()); + $cacheStrategy->update($masterResponse); + + $this->assertTrue($masterResponse->isValidateable()); + } + + public function testMasterResponseWithExpirationIsUnchangedWhenThereIsNoEmbeddedResponse() + { + $cacheStrategy = new ResponseCacheStrategy(); + + $masterResponse = new Response(); + $masterResponse->setSharedMaxAge(3600); + $cacheStrategy->update($masterResponse); + + $this->assertTrue($masterResponse->isFresh()); + } + + public function testMasterResponseIsNotCacheableWhenEmbeddedResponseIsNotCacheable() + { + $cacheStrategy = new ResponseCacheStrategy(); + + $masterResponse = new Response(); + $masterResponse->setSharedMaxAge(3600); // Public, cacheable + + /* This response has no validation or expiration information. + That makes it uncacheable, it is always stale. + (It does *not* make this private, though.) */ + $embeddedResponse = new Response(); + $this->assertFalse($embeddedResponse->isFresh()); // not fresh, as no lifetime is provided + + $cacheStrategy->add($embeddedResponse); + $cacheStrategy->update($masterResponse); + + $this->assertTrue($masterResponse->headers->hasCacheControlDirective('no-cache')); + $this->assertTrue($masterResponse->headers->hasCacheControlDirective('must-revalidate')); + $this->assertFalse($masterResponse->isFresh()); + } + + public function testEmbeddingPrivateResponseMakesMainResponsePrivate() + { + $cacheStrategy = new ResponseCacheStrategy(); + + $masterResponse = new Response(); + $masterResponse->setSharedMaxAge(3600); // public, cacheable + + // The embedded response might for example contain per-user data that remains valid for 60 seconds + $embeddedResponse = new Response(); + $embeddedResponse->setPrivate(); + $embeddedResponse->setMaxAge(60); // this would implicitly set "private" as well, but let's be explicit + + $cacheStrategy->add($embeddedResponse); + $cacheStrategy->update($masterResponse); + + $this->assertTrue($masterResponse->headers->hasCacheControlDirective('private')); + // Not sure if we should pass "max-age: 60" in this case, as long as the response is private and + // that's the more conservative of both the master and embedded response...? + } + + public function testResponseIsExiprableWhenEmbeddedResponseCombinesExpiryAndValidation() + { + /* When "expiration wins over validation" (https://symfony.com/doc/current/http_cache/validation.html) + * and both the main and embedded response provide s-maxage, then the more restricting value of both + * should be fine, regardless of whether the embedded response can be validated later on or must be + * completely regenerated. + */ + $cacheStrategy = new ResponseCacheStrategy(); + + $masterResponse = new Response(); + $masterResponse->setSharedMaxAge(3600); + + $embeddedResponse = new Response(); + $embeddedResponse->setSharedMaxAge(60); + $embeddedResponse->setEtag('foo'); + + $cacheStrategy->add($embeddedResponse); + $cacheStrategy->update($masterResponse); + + $this->assertSame('60', $masterResponse->headers->getCacheControlDirective('s-maxage')); + } + + public function testResponseIsExpirableButNotValidateableWhenMasterResponseCombinesExpirationAndValidation() + { + $cacheStrategy = new ResponseCacheStrategy(); + + $masterResponse = new Response(); + $masterResponse->setSharedMaxAge(3600); + $masterResponse->setEtag('foo'); + $masterResponse->setLastModified(new \DateTime()); + + $embeddedResponse = new Response(); + $embeddedResponse->setSharedMaxAge(60); + + $cacheStrategy->add($embeddedResponse); + $cacheStrategy->update($masterResponse); + + $this->assertSame('60', $masterResponse->headers->getCacheControlDirective('s-maxage')); + $this->assertFalse($masterResponse->isValidateable()); + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/Tests/HttpCache/SsiTest.php b/lib/silex/vendor/symfony/http-kernel/Tests/HttpCache/SsiTest.php new file mode 100644 index 000000000..1079d37a2 --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Tests/HttpCache/SsiTest.php @@ -0,0 +1,215 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Tests\HttpCache; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\HttpCache\Ssi; + +class SsiTest extends TestCase +{ + public function testHasSurrogateSsiCapability() + { + $ssi = new Ssi(); + + $request = Request::create('/'); + $request->headers->set('Surrogate-Capability', 'abc="SSI/1.0"'); + $this->assertTrue($ssi->hasSurrogateCapability($request)); + + $request = Request::create('/'); + $request->headers->set('Surrogate-Capability', 'foobar'); + $this->assertFalse($ssi->hasSurrogateCapability($request)); + + $request = Request::create('/'); + $this->assertFalse($ssi->hasSurrogateCapability($request)); + } + + public function testAddSurrogateSsiCapability() + { + $ssi = new Ssi(); + + $request = Request::create('/'); + $ssi->addSurrogateCapability($request); + $this->assertEquals('symfony="SSI/1.0"', $request->headers->get('Surrogate-Capability')); + + $ssi->addSurrogateCapability($request); + $this->assertEquals('symfony="SSI/1.0", symfony="SSI/1.0"', $request->headers->get('Surrogate-Capability')); + } + + public function testAddSurrogateControl() + { + $ssi = new Ssi(); + + $response = new Response('foo '); + $ssi->addSurrogateControl($response); + $this->assertEquals('content="SSI/1.0"', $response->headers->get('Surrogate-Control')); + + $response = new Response('foo'); + $ssi->addSurrogateControl($response); + $this->assertEquals('', $response->headers->get('Surrogate-Control')); + } + + public function testNeedsSsiParsing() + { + $ssi = new Ssi(); + + $response = new Response(); + $response->headers->set('Surrogate-Control', 'content="SSI/1.0"'); + $this->assertTrue($ssi->needsParsing($response)); + + $response = new Response(); + $this->assertFalse($ssi->needsParsing($response)); + } + + public function testRenderIncludeTag() + { + $ssi = new Ssi(); + + $this->assertEquals('', $ssi->renderIncludeTag('/', '/alt', true)); + $this->assertEquals('', $ssi->renderIncludeTag('/', '/alt', false)); + $this->assertEquals('', $ssi->renderIncludeTag('/')); + } + + public function testProcessDoesNothingIfContentTypeIsNotHtml() + { + $ssi = new Ssi(); + + $request = Request::create('/'); + $response = new Response(); + $response->headers->set('Content-Type', 'text/plain'); + $ssi->process($request, $response); + + $this->assertFalse($response->headers->has('x-body-eval')); + } + + public function testProcess() + { + $ssi = new Ssi(); + + $request = Request::create('/'); + $response = new Response('foo '); + $ssi->process($request, $response); + + $this->assertEquals('foo surrogate->handle($this, \'...\', \'\', false) ?>'."\n", $response->getContent()); + $this->assertEquals('SSI', $response->headers->get('x-body-eval')); + + $response = new Response('foo '); + $ssi->process($request, $response); + + $this->assertEquals("foo surrogate->handle(\$this, 'foo\\'', '', false) ?>"."\n", $response->getContent()); + } + + public function testProcessEscapesPhpTags() + { + $ssi = new Ssi(); + + $request = Request::create('/'); + $response = new Response(''); + $ssi->process($request, $response); + + $this->assertEquals('php cript language=php>', $response->getContent()); + } + + /** + * @expectedException \RuntimeException + */ + public function testProcessWhenNoSrcInAnSsi() + { + $ssi = new Ssi(); + + $request = Request::create('/'); + $response = new Response('foo '); + $ssi->process($request, $response); + } + + public function testProcessRemoveSurrogateControlHeader() + { + $ssi = new Ssi(); + + $request = Request::create('/'); + $response = new Response('foo '); + $response->headers->set('Surrogate-Control', 'content="SSI/1.0"'); + $ssi->process($request, $response); + $this->assertEquals('SSI', $response->headers->get('x-body-eval')); + + $response->headers->set('Surrogate-Control', 'no-store, content="SSI/1.0"'); + $ssi->process($request, $response); + $this->assertEquals('SSI', $response->headers->get('x-body-eval')); + $this->assertEquals('no-store', $response->headers->get('surrogate-control')); + + $response->headers->set('Surrogate-Control', 'content="SSI/1.0", no-store'); + $ssi->process($request, $response); + $this->assertEquals('SSI', $response->headers->get('x-body-eval')); + $this->assertEquals('no-store', $response->headers->get('surrogate-control')); + } + + public function testHandle() + { + $ssi = new Ssi(); + $cache = $this->getCache(Request::create('/'), new Response('foo')); + $this->assertEquals('foo', $ssi->handle($cache, '/', '/alt', true)); + } + + /** + * @expectedException \RuntimeException + */ + public function testHandleWhenResponseIsNot200() + { + $ssi = new Ssi(); + $response = new Response('foo'); + $response->setStatusCode(404); + $cache = $this->getCache(Request::create('/'), $response); + $ssi->handle($cache, '/', '/alt', false); + } + + public function testHandleWhenResponseIsNot200AndErrorsAreIgnored() + { + $ssi = new Ssi(); + $response = new Response('foo'); + $response->setStatusCode(404); + $cache = $this->getCache(Request::create('/'), $response); + $this->assertEquals('', $ssi->handle($cache, '/', '/alt', true)); + } + + public function testHandleWhenResponseIsNot200AndAltIsPresent() + { + $ssi = new Ssi(); + $response1 = new Response('foo'); + $response1->setStatusCode(404); + $response2 = new Response('bar'); + $cache = $this->getCache(Request::create('/'), array($response1, $response2)); + $this->assertEquals('bar', $ssi->handle($cache, '/', '/alt', false)); + } + + protected function getCache($request, $response) + { + $cache = $this->getMockBuilder('Symfony\Component\HttpKernel\HttpCache\HttpCache')->setMethods(array('getRequest', 'handle'))->disableOriginalConstructor()->getMock(); + $cache->expects($this->any()) + ->method('getRequest') + ->will($this->returnValue($request)) + ; + if (is_array($response)) { + $cache->expects($this->any()) + ->method('handle') + ->will(call_user_func_array(array($this, 'onConsecutiveCalls'), $response)) + ; + } else { + $cache->expects($this->any()) + ->method('handle') + ->will($this->returnValue($response)) + ; + } + + return $cache; + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/Tests/HttpCache/StoreTest.php b/lib/silex/vendor/symfony/http-kernel/Tests/HttpCache/StoreTest.php new file mode 100644 index 000000000..cef019167 --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Tests/HttpCache/StoreTest.php @@ -0,0 +1,301 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Tests\HttpCache; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\HttpCache\Store; + +class StoreTest extends TestCase +{ + protected $request; + protected $response; + + /** + * @var Store + */ + protected $store; + + protected function setUp() + { + $this->request = Request::create('/'); + $this->response = new Response('hello world', 200, array()); + + HttpCacheTestCase::clearDirectory(sys_get_temp_dir().'/http_cache'); + + $this->store = new Store(sys_get_temp_dir().'/http_cache'); + } + + protected function tearDown() + { + $this->store = null; + $this->request = null; + $this->response = null; + + HttpCacheTestCase::clearDirectory(sys_get_temp_dir().'/http_cache'); + } + + public function testReadsAnEmptyArrayWithReadWhenNothingCachedAtKey() + { + $this->assertEmpty($this->getStoreMetadata('/nothing')); + } + + public function testUnlockFileThatDoesExist() + { + $cacheKey = $this->storeSimpleEntry(); + $this->store->lock($this->request); + + $this->assertTrue($this->store->unlock($this->request)); + } + + public function testUnlockFileThatDoesNotExist() + { + $this->assertFalse($this->store->unlock($this->request)); + } + + public function testRemovesEntriesForKeyWithPurge() + { + $request = Request::create('/foo'); + $this->store->write($request, new Response('foo')); + + $metadata = $this->getStoreMetadata($request); + $this->assertNotEmpty($metadata); + + $this->assertTrue($this->store->purge('/foo')); + $this->assertEmpty($this->getStoreMetadata($request)); + + // cached content should be kept after purging + $path = $this->store->getPath($metadata[0][1]['x-content-digest'][0]); + $this->assertTrue(is_file($path)); + + $this->assertFalse($this->store->purge('/bar')); + } + + public function testStoresACacheEntry() + { + $cacheKey = $this->storeSimpleEntry(); + + $this->assertNotEmpty($this->getStoreMetadata($cacheKey)); + } + + public function testSetsTheXContentDigestResponseHeaderBeforeStoring() + { + $cacheKey = $this->storeSimpleEntry(); + $entries = $this->getStoreMetadata($cacheKey); + list($req, $res) = $entries[0]; + + $this->assertEquals('en9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08', $res['x-content-digest'][0]); + } + + public function testFindsAStoredEntryWithLookup() + { + $this->storeSimpleEntry(); + $response = $this->store->lookup($this->request); + + $this->assertNotNull($response); + $this->assertInstanceOf('Symfony\Component\HttpFoundation\Response', $response); + } + + public function testDoesNotFindAnEntryWithLookupWhenNoneExists() + { + $request = Request::create('/test', 'get', array(), array(), array(), array('HTTP_FOO' => 'Foo', 'HTTP_BAR' => 'Bar')); + + $this->assertNull($this->store->lookup($request)); + } + + public function testCanonizesUrlsForCacheKeys() + { + $this->storeSimpleEntry($path = '/test?x=y&p=q'); + $hitsReq = Request::create($path); + $missReq = Request::create('/test?p=x'); + + $this->assertNotNull($this->store->lookup($hitsReq)); + $this->assertNull($this->store->lookup($missReq)); + } + + public function testDoesNotFindAnEntryWithLookupWhenTheBodyDoesNotExist() + { + $this->storeSimpleEntry(); + $this->assertNotNull($this->response->headers->get('X-Content-Digest')); + $path = $this->getStorePath($this->response->headers->get('X-Content-Digest')); + @unlink($path); + $this->assertNull($this->store->lookup($this->request)); + } + + public function testRestoresResponseHeadersProperlyWithLookup() + { + $this->storeSimpleEntry(); + $response = $this->store->lookup($this->request); + + $this->assertEquals($response->headers->all(), array_merge(array('content-length' => 4, 'x-body-file' => array($this->getStorePath($response->headers->get('X-Content-Digest')))), $this->response->headers->all())); + } + + public function testRestoresResponseContentFromEntityStoreWithLookup() + { + $this->storeSimpleEntry(); + $response = $this->store->lookup($this->request); + $this->assertEquals($this->getStorePath('en'.hash('sha256', 'test')), $response->getContent()); + } + + public function testInvalidatesMetaAndEntityStoreEntriesWithInvalidate() + { + $this->storeSimpleEntry(); + $this->store->invalidate($this->request); + $response = $this->store->lookup($this->request); + $this->assertInstanceOf('Symfony\Component\HttpFoundation\Response', $response); + $this->assertFalse($response->isFresh()); + } + + public function testSucceedsQuietlyWhenInvalidateCalledWithNoMatchingEntries() + { + $req = Request::create('/test'); + $this->store->invalidate($req); + $this->assertNull($this->store->lookup($this->request)); + } + + public function testDoesNotReturnEntriesThatVaryWithLookup() + { + $req1 = Request::create('/test', 'get', array(), array(), array(), array('HTTP_FOO' => 'Foo', 'HTTP_BAR' => 'Bar')); + $req2 = Request::create('/test', 'get', array(), array(), array(), array('HTTP_FOO' => 'Bling', 'HTTP_BAR' => 'Bam')); + $res = new Response('test', 200, array('Vary' => 'Foo Bar')); + $this->store->write($req1, $res); + + $this->assertNull($this->store->lookup($req2)); + } + + public function testDoesNotReturnEntriesThatSlightlyVaryWithLookup() + { + $req1 = Request::create('/test', 'get', array(), array(), array(), array('HTTP_FOO' => 'Foo', 'HTTP_BAR' => 'Bar')); + $req2 = Request::create('/test', 'get', array(), array(), array(), array('HTTP_FOO' => 'Foo', 'HTTP_BAR' => 'Bam')); + $res = new Response('test', 200, array('Vary' => array('Foo', 'Bar'))); + $this->store->write($req1, $res); + + $this->assertNull($this->store->lookup($req2)); + } + + public function testStoresMultipleResponsesForEachVaryCombination() + { + $req1 = Request::create('/test', 'get', array(), array(), array(), array('HTTP_FOO' => 'Foo', 'HTTP_BAR' => 'Bar')); + $res1 = new Response('test 1', 200, array('Vary' => 'Foo Bar')); + $key = $this->store->write($req1, $res1); + + $req2 = Request::create('/test', 'get', array(), array(), array(), array('HTTP_FOO' => 'Bling', 'HTTP_BAR' => 'Bam')); + $res2 = new Response('test 2', 200, array('Vary' => 'Foo Bar')); + $this->store->write($req2, $res2); + + $req3 = Request::create('/test', 'get', array(), array(), array(), array('HTTP_FOO' => 'Baz', 'HTTP_BAR' => 'Boom')); + $res3 = new Response('test 3', 200, array('Vary' => 'Foo Bar')); + $this->store->write($req3, $res3); + + $this->assertEquals($this->getStorePath('en'.hash('sha256', 'test 3')), $this->store->lookup($req3)->getContent()); + $this->assertEquals($this->getStorePath('en'.hash('sha256', 'test 2')), $this->store->lookup($req2)->getContent()); + $this->assertEquals($this->getStorePath('en'.hash('sha256', 'test 1')), $this->store->lookup($req1)->getContent()); + + $this->assertCount(3, $this->getStoreMetadata($key)); + } + + public function testOverwritesNonVaryingResponseWithStore() + { + $req1 = Request::create('/test', 'get', array(), array(), array(), array('HTTP_FOO' => 'Foo', 'HTTP_BAR' => 'Bar')); + $res1 = new Response('test 1', 200, array('Vary' => 'Foo Bar')); + $key = $this->store->write($req1, $res1); + $this->assertEquals($this->getStorePath('en'.hash('sha256', 'test 1')), $this->store->lookup($req1)->getContent()); + + $req2 = Request::create('/test', 'get', array(), array(), array(), array('HTTP_FOO' => 'Bling', 'HTTP_BAR' => 'Bam')); + $res2 = new Response('test 2', 200, array('Vary' => 'Foo Bar')); + $this->store->write($req2, $res2); + $this->assertEquals($this->getStorePath('en'.hash('sha256', 'test 2')), $this->store->lookup($req2)->getContent()); + + $req3 = Request::create('/test', 'get', array(), array(), array(), array('HTTP_FOO' => 'Foo', 'HTTP_BAR' => 'Bar')); + $res3 = new Response('test 3', 200, array('Vary' => 'Foo Bar')); + $key = $this->store->write($req3, $res3); + $this->assertEquals($this->getStorePath('en'.hash('sha256', 'test 3')), $this->store->lookup($req3)->getContent()); + + $this->assertCount(2, $this->getStoreMetadata($key)); + } + + public function testLocking() + { + $req = Request::create('/test', 'get', array(), array(), array(), array('HTTP_FOO' => 'Foo', 'HTTP_BAR' => 'Bar')); + $this->assertTrue($this->store->lock($req)); + + $path = $this->store->lock($req); + $this->assertTrue($this->store->isLocked($req)); + + $this->store->unlock($req); + $this->assertFalse($this->store->isLocked($req)); + } + + public function testPurgeHttps() + { + $request = Request::create('https://example.com/foo'); + $this->store->write($request, new Response('foo')); + + $this->assertNotEmpty($this->getStoreMetadata($request)); + + $this->assertTrue($this->store->purge('https://example.com/foo')); + $this->assertEmpty($this->getStoreMetadata($request)); + } + + public function testPurgeHttpAndHttps() + { + $requestHttp = Request::create('https://example.com/foo'); + $this->store->write($requestHttp, new Response('foo')); + + $requestHttps = Request::create('http://example.com/foo'); + $this->store->write($requestHttps, new Response('foo')); + + $this->assertNotEmpty($this->getStoreMetadata($requestHttp)); + $this->assertNotEmpty($this->getStoreMetadata($requestHttps)); + + $this->assertTrue($this->store->purge('http://example.com/foo')); + $this->assertEmpty($this->getStoreMetadata($requestHttp)); + $this->assertEmpty($this->getStoreMetadata($requestHttps)); + } + + protected function storeSimpleEntry($path = null, $headers = array()) + { + if (null === $path) { + $path = '/test'; + } + + $this->request = Request::create($path, 'get', array(), array(), array(), $headers); + $this->response = new Response('test', 200, array('Cache-Control' => 'max-age=420')); + + return $this->store->write($this->request, $this->response); + } + + protected function getStoreMetadata($key) + { + $r = new \ReflectionObject($this->store); + $m = $r->getMethod('getMetadata'); + $m->setAccessible(true); + + if ($key instanceof Request) { + $m1 = $r->getMethod('getCacheKey'); + $m1->setAccessible(true); + $key = $m1->invoke($this->store, $key); + } + + return $m->invoke($this->store, $key); + } + + protected function getStorePath($key) + { + $r = new \ReflectionObject($this->store); + $m = $r->getMethod('getPath'); + $m->setAccessible(true); + + return $m->invoke($this->store, $key); + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/Tests/HttpCache/TestHttpKernel.php b/lib/silex/vendor/symfony/http-kernel/Tests/HttpCache/TestHttpKernel.php new file mode 100644 index 000000000..946c7a31c --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Tests/HttpCache/TestHttpKernel.php @@ -0,0 +1,92 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Tests\HttpCache; + +use Symfony\Component\HttpKernel\Controller\ArgumentResolverInterface; +use Symfony\Component\HttpKernel\HttpKernel; +use Symfony\Component\HttpKernel\HttpKernelInterface; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\Controller\ControllerResolverInterface; +use Symfony\Component\EventDispatcher\EventDispatcher; + +class TestHttpKernel extends HttpKernel implements ControllerResolverInterface, ArgumentResolverInterface +{ + protected $body; + protected $status; + protected $headers; + protected $called = false; + protected $customizer; + protected $catch = false; + protected $backendRequest; + + public function __construct($body, $status, $headers, \Closure $customizer = null) + { + $this->body = $body; + $this->status = $status; + $this->headers = $headers; + $this->customizer = $customizer; + + parent::__construct(new EventDispatcher(), $this, null, $this); + } + + public function getBackendRequest() + { + return $this->backendRequest; + } + + public function handle(Request $request, $type = HttpKernelInterface::MASTER_REQUEST, $catch = false) + { + $this->catch = $catch; + $this->backendRequest = $request; + + return parent::handle($request, $type, $catch); + } + + public function isCatchingExceptions() + { + return $this->catch; + } + + public function getController(Request $request) + { + return array($this, 'callController'); + } + + public function getArguments(Request $request, $controller) + { + return array($request); + } + + public function callController(Request $request) + { + $this->called = true; + + $response = new Response($this->body, $this->status, $this->headers); + + if (null !== $customizer = $this->customizer) { + $customizer($request, $response); + } + + return $response; + } + + public function hasBeenCalled() + { + return $this->called; + } + + public function reset() + { + $this->called = false; + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/Tests/HttpCache/TestMultipleHttpKernel.php b/lib/silex/vendor/symfony/http-kernel/Tests/HttpCache/TestMultipleHttpKernel.php new file mode 100644 index 000000000..926d8daf5 --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Tests/HttpCache/TestMultipleHttpKernel.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\HttpKernel\Tests\HttpCache; + +use Symfony\Component\HttpKernel\Controller\ArgumentResolverInterface; +use Symfony\Component\HttpKernel\HttpKernel; +use Symfony\Component\HttpKernel\HttpKernelInterface; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\Controller\ControllerResolverInterface; +use Symfony\Component\EventDispatcher\EventDispatcher; + +class TestMultipleHttpKernel extends HttpKernel implements ControllerResolverInterface, ArgumentResolverInterface +{ + protected $bodies = array(); + protected $statuses = array(); + protected $headers = array(); + protected $called = false; + protected $backendRequest; + + public function __construct($responses) + { + foreach ($responses as $response) { + $this->bodies[] = $response['body']; + $this->statuses[] = $response['status']; + $this->headers[] = $response['headers']; + } + + parent::__construct(new EventDispatcher(), $this, null, $this); + } + + public function getBackendRequest() + { + return $this->backendRequest; + } + + public function handle(Request $request, $type = HttpKernelInterface::MASTER_REQUEST, $catch = false) + { + $this->backendRequest = $request; + + return parent::handle($request, $type, $catch); + } + + public function getController(Request $request) + { + return array($this, 'callController'); + } + + public function getArguments(Request $request, $controller) + { + return array($request); + } + + public function callController(Request $request) + { + $this->called = true; + + $response = new Response(array_shift($this->bodies), array_shift($this->statuses), array_shift($this->headers)); + + return $response; + } + + public function hasBeenCalled() + { + return $this->called; + } + + public function reset() + { + $this->called = false; + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/Tests/HttpKernelTest.php b/lib/silex/vendor/symfony/http-kernel/Tests/HttpKernelTest.php new file mode 100644 index 000000000..7aed26aa5 --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Tests/HttpKernelTest.php @@ -0,0 +1,403 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Tests; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; +use Symfony\Component\HttpFoundation\RequestStack; +use Symfony\Component\HttpKernel\Controller\ArgumentResolverInterface; +use Symfony\Component\HttpKernel\Controller\ControllerResolverInterface; +use Symfony\Component\HttpKernel\Event\FilterControllerArgumentsEvent; +use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent; +use Symfony\Component\HttpKernel\HttpKernel; +use Symfony\Component\HttpKernel\HttpKernelInterface; +use Symfony\Component\HttpKernel\KernelEvents; +use Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException; +use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpFoundation\RedirectResponse; +use Symfony\Component\EventDispatcher\EventDispatcher; + +class HttpKernelTest extends TestCase +{ + /** + * @expectedException \RuntimeException + */ + public function testHandleWhenControllerThrowsAnExceptionAndCatchIsTrue() + { + $kernel = $this->getHttpKernel(new EventDispatcher(), function () { throw new \RuntimeException(); }); + + $kernel->handle(new Request(), HttpKernelInterface::MASTER_REQUEST, true); + } + + /** + * @expectedException \RuntimeException + */ + public function testHandleWhenControllerThrowsAnExceptionAndCatchIsFalseAndNoListenerIsRegistered() + { + $kernel = $this->getHttpKernel(new EventDispatcher(), function () { throw new \RuntimeException(); }); + + $kernel->handle(new Request(), HttpKernelInterface::MASTER_REQUEST, false); + } + + public function testHandleWhenControllerThrowsAnExceptionAndCatchIsTrueWithAHandlingListener() + { + $dispatcher = new EventDispatcher(); + $dispatcher->addListener(KernelEvents::EXCEPTION, function ($event) { + $event->setResponse(new Response($event->getException()->getMessage())); + }); + + $kernel = $this->getHttpKernel($dispatcher, function () { throw new \RuntimeException('foo'); }); + $response = $kernel->handle(new Request(), HttpKernelInterface::MASTER_REQUEST, true); + + $this->assertEquals('500', $response->getStatusCode()); + $this->assertEquals('foo', $response->getContent()); + } + + public function testHandleWhenControllerThrowsAnExceptionAndCatchIsTrueWithANonHandlingListener() + { + $exception = new \RuntimeException(); + + $dispatcher = new EventDispatcher(); + $dispatcher->addListener(KernelEvents::EXCEPTION, function ($event) { + // should set a response, but does not + }); + + $kernel = $this->getHttpKernel($dispatcher, function () use ($exception) { throw $exception; }); + + try { + $kernel->handle(new Request(), HttpKernelInterface::MASTER_REQUEST, true); + $this->fail('LogicException expected'); + } catch (\RuntimeException $e) { + $this->assertSame($exception, $e); + } + } + + public function testHandleExceptionWithARedirectionResponse() + { + $dispatcher = new EventDispatcher(); + $dispatcher->addListener(KernelEvents::EXCEPTION, function ($event) { + $event->setResponse(new RedirectResponse('/login', 301)); + }); + + $kernel = $this->getHttpKernel($dispatcher, function () { throw new AccessDeniedHttpException(); }); + $response = $kernel->handle(new Request()); + + $this->assertEquals('301', $response->getStatusCode()); + $this->assertEquals('/login', $response->headers->get('Location')); + } + + public function testHandleHttpException() + { + $dispatcher = new EventDispatcher(); + $dispatcher->addListener(KernelEvents::EXCEPTION, function ($event) { + $event->setResponse(new Response($event->getException()->getMessage())); + }); + + $kernel = $this->getHttpKernel($dispatcher, function () { throw new MethodNotAllowedHttpException(array('POST')); }); + $response = $kernel->handle(new Request()); + + $this->assertEquals('405', $response->getStatusCode()); + $this->assertEquals('POST', $response->headers->get('Allow')); + } + + /** + * @group legacy + * @dataProvider getStatusCodes + */ + public function testLegacyHandleWhenAnExceptionIsHandledWithASpecificStatusCode($responseStatusCode, $expectedStatusCode) + { + $dispatcher = new EventDispatcher(); + $dispatcher->addListener(KernelEvents::EXCEPTION, function ($event) use ($responseStatusCode, $expectedStatusCode) { + $event->setResponse(new Response('', $responseStatusCode, array('X-Status-Code' => $expectedStatusCode))); + }); + + $kernel = $this->getHttpKernel($dispatcher, function () { throw new \RuntimeException(); }); + $response = $kernel->handle(new Request()); + + $this->assertEquals($expectedStatusCode, $response->getStatusCode()); + $this->assertFalse($response->headers->has('X-Status-Code')); + } + + public function getStatusCodes() + { + return array( + array(200, 404), + array(404, 200), + array(301, 200), + array(500, 200), + ); + } + + /** + * @dataProvider getSpecificStatusCodes + */ + public function testHandleWhenAnExceptionIsHandledWithASpecificStatusCode($expectedStatusCode) + { + $dispatcher = new EventDispatcher(); + $dispatcher->addListener(KernelEvents::EXCEPTION, function (GetResponseForExceptionEvent $event) use ($expectedStatusCode) { + $event->allowCustomResponseCode(); + $event->setResponse(new Response('', $expectedStatusCode)); + }); + + $kernel = $this->getHttpKernel($dispatcher, function () { throw new \RuntimeException(); }); + $response = $kernel->handle(new Request()); + + $this->assertEquals($expectedStatusCode, $response->getStatusCode()); + } + + public function getSpecificStatusCodes() + { + return array( + array(200), + array(302), + array(403), + ); + } + + public function testHandleWhenAListenerReturnsAResponse() + { + $dispatcher = new EventDispatcher(); + $dispatcher->addListener(KernelEvents::REQUEST, function ($event) { + $event->setResponse(new Response('hello')); + }); + + $kernel = $this->getHttpKernel($dispatcher); + + $this->assertEquals('hello', $kernel->handle(new Request())->getContent()); + } + + /** + * @expectedException \Symfony\Component\HttpKernel\Exception\NotFoundHttpException + */ + public function testHandleWhenNoControllerIsFound() + { + $dispatcher = new EventDispatcher(); + $kernel = $this->getHttpKernel($dispatcher, false); + + $kernel->handle(new Request()); + } + + public function testHandleWhenTheControllerIsAClosure() + { + $response = new Response('foo'); + $dispatcher = new EventDispatcher(); + $kernel = $this->getHttpKernel($dispatcher, function () use ($response) { return $response; }); + + $this->assertSame($response, $kernel->handle(new Request())); + } + + public function testHandleWhenTheControllerIsAnObjectWithInvoke() + { + $dispatcher = new EventDispatcher(); + $kernel = $this->getHttpKernel($dispatcher, new Controller()); + + $this->assertResponseEquals(new Response('foo'), $kernel->handle(new Request())); + } + + public function testHandleWhenTheControllerIsAFunction() + { + $dispatcher = new EventDispatcher(); + $kernel = $this->getHttpKernel($dispatcher, 'Symfony\Component\HttpKernel\Tests\controller_func'); + + $this->assertResponseEquals(new Response('foo'), $kernel->handle(new Request())); + } + + public function testHandleWhenTheControllerIsAnArray() + { + $dispatcher = new EventDispatcher(); + $kernel = $this->getHttpKernel($dispatcher, array(new Controller(), 'controller')); + + $this->assertResponseEquals(new Response('foo'), $kernel->handle(new Request())); + } + + public function testHandleWhenTheControllerIsAStaticArray() + { + $dispatcher = new EventDispatcher(); + $kernel = $this->getHttpKernel($dispatcher, array('Symfony\Component\HttpKernel\Tests\Controller', 'staticcontroller')); + + $this->assertResponseEquals(new Response('foo'), $kernel->handle(new Request())); + } + + /** + * @expectedException \LogicException + */ + public function testHandleWhenTheControllerDoesNotReturnAResponse() + { + $dispatcher = new EventDispatcher(); + $kernel = $this->getHttpKernel($dispatcher, function () { return 'foo'; }); + + $kernel->handle(new Request()); + } + + public function testHandleWhenTheControllerDoesNotReturnAResponseButAViewIsRegistered() + { + $dispatcher = new EventDispatcher(); + $dispatcher->addListener(KernelEvents::VIEW, function ($event) { + $event->setResponse(new Response($event->getControllerResult())); + }); + + $kernel = $this->getHttpKernel($dispatcher, function () { return 'foo'; }); + + $this->assertEquals('foo', $kernel->handle(new Request())->getContent()); + } + + public function testHandleWithAResponseListener() + { + $dispatcher = new EventDispatcher(); + $dispatcher->addListener(KernelEvents::RESPONSE, function ($event) { + $event->setResponse(new Response('foo')); + }); + $kernel = $this->getHttpKernel($dispatcher); + + $this->assertEquals('foo', $kernel->handle(new Request())->getContent()); + } + + public function testHandleAllowChangingControllerArguments() + { + $dispatcher = new EventDispatcher(); + $dispatcher->addListener(KernelEvents::CONTROLLER_ARGUMENTS, function (FilterControllerArgumentsEvent $event) { + $event->setArguments(array('foo')); + }); + + $kernel = $this->getHttpKernel($dispatcher, function ($content) { return new Response($content); }); + + $this->assertResponseEquals(new Response('foo'), $kernel->handle(new Request())); + } + + public function testHandleAllowChangingControllerAndArguments() + { + $dispatcher = new EventDispatcher(); + $dispatcher->addListener(KernelEvents::CONTROLLER_ARGUMENTS, function (FilterControllerArgumentsEvent $event) { + $oldController = $event->getController(); + $oldArguments = $event->getArguments(); + + $newController = function ($id) use ($oldController, $oldArguments) { + $response = call_user_func_array($oldController, $oldArguments); + + $response->headers->set('X-Id', $id); + + return $response; + }; + + $event->setController($newController); + $event->setArguments(array('bar')); + }); + + $kernel = $this->getHttpKernel($dispatcher, function ($content) { return new Response($content); }, null, array('foo')); + + $this->assertResponseEquals(new Response('foo', 200, array('X-Id' => 'bar')), $kernel->handle(new Request())); + } + + public function testTerminate() + { + $dispatcher = new EventDispatcher(); + $kernel = $this->getHttpKernel($dispatcher); + $dispatcher->addListener(KernelEvents::TERMINATE, function ($event) use (&$called, &$capturedKernel, &$capturedRequest, &$capturedResponse) { + $called = true; + $capturedKernel = $event->getKernel(); + $capturedRequest = $event->getRequest(); + $capturedResponse = $event->getResponse(); + }); + + $kernel->terminate($request = Request::create('/'), $response = new Response()); + $this->assertTrue($called); + $this->assertEquals($kernel, $capturedKernel); + $this->assertEquals($request, $capturedRequest); + $this->assertEquals($response, $capturedResponse); + } + + public function testVerifyRequestStackPushPopDuringHandle() + { + $request = new Request(); + + $stack = $this->getMockBuilder('Symfony\Component\HttpFoundation\RequestStack')->setMethods(array('push', 'pop'))->getMock(); + $stack->expects($this->at(0))->method('push')->with($this->equalTo($request)); + $stack->expects($this->at(1))->method('pop'); + + $dispatcher = new EventDispatcher(); + $kernel = $this->getHttpKernel($dispatcher, null, $stack); + + $kernel->handle($request, HttpKernelInterface::MASTER_REQUEST); + } + + /** + * @expectedException \Symfony\Component\HttpKernel\Exception\BadRequestHttpException + */ + public function testInconsistentClientIpsOnMasterRequests() + { + $request = new Request(); + $request->setTrustedProxies(array('1.1.1.1'), Request::HEADER_X_FORWARDED_FOR | Request::HEADER_FORWARDED); + $request->server->set('REMOTE_ADDR', '1.1.1.1'); + $request->headers->set('FORWARDED', 'for=2.2.2.2'); + $request->headers->set('X_FORWARDED_FOR', '3.3.3.3'); + + $dispatcher = new EventDispatcher(); + $dispatcher->addListener(KernelEvents::REQUEST, function ($event) { + $event->getRequest()->getClientIp(); + }); + + $kernel = $this->getHttpKernel($dispatcher); + $kernel->handle($request, $kernel::MASTER_REQUEST, false); + } + + private function getHttpKernel(EventDispatcherInterface $eventDispatcher, $controller = null, RequestStack $requestStack = null, array $arguments = array()) + { + if (null === $controller) { + $controller = function () { return new Response('Hello'); }; + } + + $controllerResolver = $this->getMockBuilder(ControllerResolverInterface::class)->getMock(); + $controllerResolver + ->expects($this->any()) + ->method('getController') + ->will($this->returnValue($controller)); + + $argumentResolver = $this->getMockBuilder(ArgumentResolverInterface::class)->getMock(); + $argumentResolver + ->expects($this->any()) + ->method('getArguments') + ->will($this->returnValue($arguments)); + + return new HttpKernel($eventDispatcher, $controllerResolver, $requestStack, $argumentResolver); + } + + private function assertResponseEquals(Response $expected, Response $actual) + { + $expected->setDate($actual->getDate()); + $this->assertEquals($expected, $actual); + } +} + +class Controller +{ + public function __invoke() + { + return new Response('foo'); + } + + public function controller() + { + return new Response('foo'); + } + + public static function staticController() + { + return new Response('foo'); + } +} + +function controller_func() +{ + return new Response('foo'); +} diff --git a/lib/silex/vendor/symfony/http-kernel/Tests/KernelTest.php b/lib/silex/vendor/symfony/http-kernel/Tests/KernelTest.php new file mode 100644 index 000000000..755e1db2b --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Tests/KernelTest.php @@ -0,0 +1,1060 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Tests; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Config\Loader\LoaderInterface; +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\Filesystem\Filesystem; +use Symfony\Component\HttpKernel\Bundle\BundleInterface; +use Symfony\Component\HttpKernel\Config\EnvParametersResource; +use Symfony\Component\HttpKernel\DependencyInjection\ResettableServicePass; +use Symfony\Component\HttpKernel\DependencyInjection\ServicesResetter; +use Symfony\Component\HttpKernel\Kernel; +use Symfony\Component\HttpKernel\HttpKernelInterface; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\Tests\Fixtures\KernelForTest; +use Symfony\Component\HttpKernel\Tests\Fixtures\KernelForOverrideName; +use Symfony\Component\HttpKernel\Tests\Fixtures\KernelWithoutBundles; +use Symfony\Component\HttpKernel\Tests\Fixtures\ResettableService; + +class KernelTest extends TestCase +{ + public static function tearDownAfterClass() + { + $fs = new Filesystem(); + $fs->remove(__DIR__.'/Fixtures/cache'); + } + + public function testConstructor() + { + $env = 'test_env'; + $debug = true; + $kernel = new KernelForTest($env, $debug); + + $this->assertEquals($env, $kernel->getEnvironment()); + $this->assertEquals($debug, $kernel->isDebug()); + $this->assertFalse($kernel->isBooted()); + $this->assertLessThanOrEqual(microtime(true), $kernel->getStartTime()); + $this->assertNull($kernel->getContainer()); + } + + public function testClone() + { + $env = 'test_env'; + $debug = true; + $kernel = new KernelForTest($env, $debug); + + $clone = clone $kernel; + + $this->assertEquals($env, $clone->getEnvironment()); + $this->assertEquals($debug, $clone->isDebug()); + $this->assertFalse($clone->isBooted()); + $this->assertLessThanOrEqual(microtime(true), $clone->getStartTime()); + $this->assertNull($clone->getContainer()); + } + + public function testInitializeContainerClearsOldContainers() + { + $fs = new Filesystem(); + $legacyContainerDir = __DIR__.'/Fixtures/cache/custom/ContainerA123456'; + $fs->mkdir($legacyContainerDir); + touch($legacyContainerDir.'.legacy'); + + $kernel = new CustomProjectDirKernel(); + $kernel->boot(); + + $containerDir = __DIR__.'/Fixtures/cache/custom/'.substr(get_class($kernel->getContainer()), 0, 16); + $this->assertTrue(unlink(__DIR__.'/Fixtures/cache/custom/FixturesCustomDebugProjectContainer.php.meta')); + $this->assertFileExists($containerDir); + $this->assertFileNotExists($containerDir.'.legacy'); + + $kernel = new CustomProjectDirKernel(function ($container) { $container->register('foo', 'stdClass')->setPublic(true); }); + $kernel->boot(); + + $this->assertFileExists($containerDir); + $this->assertFileExists($containerDir.'.legacy'); + + $this->assertFileNotExists($legacyContainerDir); + $this->assertFileNotExists($legacyContainerDir.'.legacy'); + } + + public function testBootInitializesBundlesAndContainer() + { + $kernel = $this->getKernel(array('initializeBundles', 'initializeContainer')); + $kernel->expects($this->once()) + ->method('initializeBundles'); + $kernel->expects($this->once()) + ->method('initializeContainer'); + + $kernel->boot(); + } + + public function testBootSetsTheContainerToTheBundles() + { + $bundle = $this->getMockBuilder('Symfony\Component\HttpKernel\Bundle\Bundle')->getMock(); + $bundle->expects($this->once()) + ->method('setContainer'); + + $kernel = $this->getKernel(array('initializeBundles', 'initializeContainer', 'getBundles')); + $kernel->expects($this->once()) + ->method('getBundles') + ->will($this->returnValue(array($bundle))); + + $kernel->boot(); + } + + public function testBootSetsTheBootedFlagToTrue() + { + // use test kernel to access isBooted() + $kernel = $this->getKernelForTest(array('initializeBundles', 'initializeContainer')); + $kernel->boot(); + + $this->assertTrue($kernel->isBooted()); + } + + /** + * @group legacy + */ + public function testClassCacheIsLoaded() + { + $kernel = $this->getKernel(array('initializeBundles', 'initializeContainer', 'doLoadClassCache')); + $kernel->loadClassCache('name', '.extension'); + $kernel->expects($this->once()) + ->method('doLoadClassCache') + ->with('name', '.extension'); + + $kernel->boot(); + } + + public function testClassCacheIsNotLoadedByDefault() + { + $kernel = $this->getKernel(array('initializeBundles', 'initializeContainer', 'doLoadClassCache')); + $kernel->expects($this->never()) + ->method('doLoadClassCache'); + + $kernel->boot(); + } + + /** + * @group legacy + */ + public function testClassCacheIsNotLoadedWhenKernelIsNotBooted() + { + $kernel = $this->getKernel(array('initializeBundles', 'initializeContainer', 'doLoadClassCache')); + $kernel->loadClassCache(); + $kernel->expects($this->never()) + ->method('doLoadClassCache'); + } + + public function testEnvParametersResourceIsAdded() + { + $container = new ContainerBuilder(); + $kernel = $this->getMockBuilder('Symfony\Component\HttpKernel\Tests\Fixtures\KernelForTest') + ->disableOriginalConstructor() + ->setMethods(array('getContainerBuilder', 'prepareContainer', 'getCacheDir', 'getLogDir')) + ->getMock(); + $kernel->expects($this->any()) + ->method('getContainerBuilder') + ->will($this->returnValue($container)); + $kernel->expects($this->any()) + ->method('prepareContainer') + ->will($this->returnValue(null)); + $kernel->expects($this->any()) + ->method('getCacheDir') + ->will($this->returnValue(sys_get_temp_dir())); + $kernel->expects($this->any()) + ->method('getLogDir') + ->will($this->returnValue(sys_get_temp_dir())); + + $reflection = new \ReflectionClass(get_class($kernel)); + $method = $reflection->getMethod('buildContainer'); + $method->setAccessible(true); + $method->invoke($kernel); + + $found = false; + foreach ($container->getResources() as $resource) { + if ($resource instanceof EnvParametersResource) { + $found = true; + break; + } + } + + $this->assertTrue($found); + } + + public function testBootKernelSeveralTimesOnlyInitializesBundlesOnce() + { + $kernel = $this->getKernel(array('initializeBundles', 'initializeContainer')); + $kernel->expects($this->once()) + ->method('initializeBundles'); + + $kernel->boot(); + $kernel->boot(); + } + + public function testShutdownCallsShutdownOnAllBundles() + { + $bundle = $this->getMockBuilder('Symfony\Component\HttpKernel\Bundle\Bundle')->getMock(); + $bundle->expects($this->once()) + ->method('shutdown'); + + $kernel = $this->getKernel(array(), array($bundle)); + + $kernel->boot(); + $kernel->shutdown(); + } + + public function testShutdownGivesNullContainerToAllBundles() + { + $bundle = $this->getMockBuilder('Symfony\Component\HttpKernel\Bundle\Bundle')->getMock(); + $bundle->expects($this->at(3)) + ->method('setContainer') + ->with(null); + + $kernel = $this->getKernel(array('getBundles')); + $kernel->expects($this->any()) + ->method('getBundles') + ->will($this->returnValue(array($bundle))); + + $kernel->boot(); + $kernel->shutdown(); + } + + public function testHandleCallsHandleOnHttpKernel() + { + $type = HttpKernelInterface::MASTER_REQUEST; + $catch = true; + $request = new Request(); + + $httpKernelMock = $this->getMockBuilder('Symfony\Component\HttpKernel\HttpKernel') + ->disableOriginalConstructor() + ->getMock(); + $httpKernelMock + ->expects($this->once()) + ->method('handle') + ->with($request, $type, $catch); + + $kernel = $this->getKernel(array('getHttpKernel')); + $kernel->expects($this->once()) + ->method('getHttpKernel') + ->will($this->returnValue($httpKernelMock)); + + $kernel->handle($request, $type, $catch); + } + + public function testHandleBootsTheKernel() + { + $type = HttpKernelInterface::MASTER_REQUEST; + $catch = true; + $request = new Request(); + + $httpKernelMock = $this->getMockBuilder('Symfony\Component\HttpKernel\HttpKernel') + ->disableOriginalConstructor() + ->getMock(); + + $kernel = $this->getKernel(array('getHttpKernel', 'boot')); + $kernel->expects($this->once()) + ->method('getHttpKernel') + ->will($this->returnValue($httpKernelMock)); + + $kernel->expects($this->once()) + ->method('boot'); + + $kernel->handle($request, $type, $catch); + } + + public function testStripComments() + { + $source = <<<'EOF' +assertEquals($expected, $output); + } + + public function testGetRootDir() + { + $kernel = new KernelForTest('test', true); + + $this->assertEquals(__DIR__.DIRECTORY_SEPARATOR.'Fixtures', realpath($kernel->getRootDir())); + } + + public function testGetName() + { + $kernel = new KernelForTest('test', true); + + $this->assertEquals('Fixtures', $kernel->getName()); + } + + public function testOverrideGetName() + { + $kernel = new KernelForOverrideName('test', true); + + $this->assertEquals('overridden', $kernel->getName()); + } + + public function testSerialize() + { + $env = 'test_env'; + $debug = true; + $kernel = new KernelForTest($env, $debug); + + $expected = serialize(array($env, $debug)); + $this->assertEquals($expected, $kernel->serialize()); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testLocateResourceThrowsExceptionWhenNameIsNotValid() + { + $this->getKernel()->locateResource('Foo'); + } + + /** + * @expectedException \RuntimeException + */ + public function testLocateResourceThrowsExceptionWhenNameIsUnsafe() + { + $this->getKernel()->locateResource('@FooBundle/../bar'); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testLocateResourceThrowsExceptionWhenBundleDoesNotExist() + { + $this->getKernel()->locateResource('@FooBundle/config/routing.xml'); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testLocateResourceThrowsExceptionWhenResourceDoesNotExist() + { + $kernel = $this->getKernel(array('getBundle')); + $kernel + ->expects($this->once()) + ->method('getBundle') + ->will($this->returnValue(array($this->getBundle(__DIR__.'/Fixtures/Bundle1Bundle')))) + ; + + $kernel->locateResource('@Bundle1Bundle/config/routing.xml'); + } + + public function testLocateResourceReturnsTheFirstThatMatches() + { + $kernel = $this->getKernel(array('getBundle')); + $kernel + ->expects($this->once()) + ->method('getBundle') + ->will($this->returnValue(array($this->getBundle(__DIR__.'/Fixtures/Bundle1Bundle')))) + ; + + $this->assertEquals(__DIR__.'/Fixtures/Bundle1Bundle/foo.txt', $kernel->locateResource('@Bundle1Bundle/foo.txt')); + } + + /** + * @group legacy + */ + public function testLocateResourceReturnsTheFirstThatMatchesWithParent() + { + $parent = $this->getBundle(__DIR__.'/Fixtures/Bundle1Bundle'); + $child = $this->getBundle(__DIR__.'/Fixtures/Bundle2Bundle'); + + $kernel = $this->getKernel(array('getBundle')); + $kernel + ->expects($this->exactly(2)) + ->method('getBundle') + ->will($this->returnValue(array($child, $parent))) + ; + + $this->assertEquals(__DIR__.'/Fixtures/Bundle2Bundle/foo.txt', $kernel->locateResource('@ParentAABundle/foo.txt')); + $this->assertEquals(__DIR__.'/Fixtures/Bundle1Bundle/bar.txt', $kernel->locateResource('@ParentAABundle/bar.txt')); + } + + /** + * @group legacy + */ + public function testLocateResourceReturnsAllMatches() + { + $parent = $this->getBundle(__DIR__.'/Fixtures/Bundle1Bundle'); + $child = $this->getBundle(__DIR__.'/Fixtures/Bundle2Bundle'); + + $kernel = $this->getKernel(array('getBundle')); + $kernel + ->expects($this->once()) + ->method('getBundle') + ->will($this->returnValue(array($child, $parent))) + ; + + $this->assertEquals(array( + __DIR__.'/Fixtures/Bundle2Bundle/foo.txt', + __DIR__.'/Fixtures/Bundle1Bundle/foo.txt', ), + $kernel->locateResource('@Bundle1Bundle/foo.txt', null, false)); + } + + /** + * @group legacy + */ + public function testLocateResourceReturnsAllMatchesBis() + { + $kernel = $this->getKernel(array('getBundle')); + $kernel + ->expects($this->once()) + ->method('getBundle') + ->will($this->returnValue(array( + $this->getBundle(__DIR__.'/Fixtures/Bundle1Bundle'), + $this->getBundle(__DIR__.'/Foobar'), + ))) + ; + + $this->assertEquals( + array(__DIR__.'/Fixtures/Bundle1Bundle/foo.txt'), + $kernel->locateResource('@Bundle1Bundle/foo.txt', null, false) + ); + } + + public function testLocateResourceIgnoresDirOnNonResource() + { + $kernel = $this->getKernel(array('getBundle')); + $kernel + ->expects($this->once()) + ->method('getBundle') + ->will($this->returnValue(array($this->getBundle(__DIR__.'/Fixtures/Bundle1Bundle')))) + ; + + $this->assertEquals( + __DIR__.'/Fixtures/Bundle1Bundle/foo.txt', + $kernel->locateResource('@Bundle1Bundle/foo.txt', __DIR__.'/Fixtures') + ); + } + + public function testLocateResourceReturnsTheDirOneForResources() + { + $kernel = $this->getKernel(array('getBundle')); + $kernel + ->expects($this->once()) + ->method('getBundle') + ->will($this->returnValue(array($this->getBundle(__DIR__.'/Fixtures/FooBundle', null, null, 'FooBundle')))) + ; + + $this->assertEquals( + __DIR__.'/Fixtures/Resources/FooBundle/foo.txt', + $kernel->locateResource('@FooBundle/Resources/foo.txt', __DIR__.'/Fixtures/Resources') + ); + } + + /** + * @group legacy + */ + public function testLocateResourceReturnsTheDirOneForResourcesAndBundleOnes() + { + $kernel = $this->getKernel(array('getBundle')); + $kernel + ->expects($this->once()) + ->method('getBundle') + ->will($this->returnValue(array($this->getBundle(__DIR__.'/Fixtures/Bundle1Bundle', null, null, 'Bundle1Bundle')))) + ; + + $this->assertEquals(array( + __DIR__.'/Fixtures/Resources/Bundle1Bundle/foo.txt', + __DIR__.'/Fixtures/Bundle1Bundle/Resources/foo.txt', ), + $kernel->locateResource('@Bundle1Bundle/Resources/foo.txt', __DIR__.'/Fixtures/Resources', false) + ); + } + + /** + * @group legacy + */ + public function testLocateResourceOverrideBundleAndResourcesFolders() + { + $parent = $this->getBundle(__DIR__.'/Fixtures/BaseBundle', null, 'BaseBundle', 'BaseBundle'); + $child = $this->getBundle(__DIR__.'/Fixtures/ChildBundle', 'ParentBundle', 'ChildBundle', 'ChildBundle'); + + $kernel = $this->getKernel(array('getBundle')); + $kernel + ->expects($this->exactly(4)) + ->method('getBundle') + ->will($this->returnValue(array($child, $parent))) + ; + + $this->assertEquals(array( + __DIR__.'/Fixtures/Resources/ChildBundle/foo.txt', + __DIR__.'/Fixtures/ChildBundle/Resources/foo.txt', + __DIR__.'/Fixtures/BaseBundle/Resources/foo.txt', + ), + $kernel->locateResource('@BaseBundle/Resources/foo.txt', __DIR__.'/Fixtures/Resources', false) + ); + + $this->assertEquals( + __DIR__.'/Fixtures/Resources/ChildBundle/foo.txt', + $kernel->locateResource('@BaseBundle/Resources/foo.txt', __DIR__.'/Fixtures/Resources') + ); + + try { + $kernel->locateResource('@BaseBundle/Resources/hide.txt', __DIR__.'/Fixtures/Resources', false); + $this->fail('Hidden resources should raise an exception when returning an array of matching paths'); + } catch (\RuntimeException $e) { + } + + try { + $kernel->locateResource('@BaseBundle/Resources/hide.txt', __DIR__.'/Fixtures/Resources', true); + $this->fail('Hidden resources should raise an exception when returning the first matching path'); + } catch (\RuntimeException $e) { + } + } + + public function testLocateResourceOnDirectories() + { + $kernel = $this->getKernel(array('getBundle')); + $kernel + ->expects($this->exactly(2)) + ->method('getBundle') + ->will($this->returnValue(array($this->getBundle(__DIR__.'/Fixtures/FooBundle', null, null, 'FooBundle')))) + ; + + $this->assertEquals( + __DIR__.'/Fixtures/Resources/FooBundle/', + $kernel->locateResource('@FooBundle/Resources/', __DIR__.'/Fixtures/Resources') + ); + $this->assertEquals( + __DIR__.'/Fixtures/Resources/FooBundle', + $kernel->locateResource('@FooBundle/Resources', __DIR__.'/Fixtures/Resources') + ); + + $kernel = $this->getKernel(array('getBundle')); + $kernel + ->expects($this->exactly(2)) + ->method('getBundle') + ->will($this->returnValue(array($this->getBundle(__DIR__.'/Fixtures/Bundle1Bundle', null, null, 'Bundle1Bundle')))) + ; + + $this->assertEquals( + __DIR__.'/Fixtures/Bundle1Bundle/Resources/', + $kernel->locateResource('@Bundle1Bundle/Resources/') + ); + $this->assertEquals( + __DIR__.'/Fixtures/Bundle1Bundle/Resources', + $kernel->locateResource('@Bundle1Bundle/Resources') + ); + } + + /** + * @group legacy + */ + public function testInitializeBundles() + { + $parent = $this->getBundle(null, null, 'ParentABundle'); + $child = $this->getBundle(null, 'ParentABundle', 'ChildABundle'); + + // use test kernel so we can access getBundleMap() + $kernel = $this->getKernelForTest(array('registerBundles')); + $kernel + ->expects($this->once()) + ->method('registerBundles') + ->will($this->returnValue(array($parent, $child))) + ; + $kernel->boot(); + + $map = $kernel->getBundleMap(); + $this->assertEquals(array($child, $parent), $map['ParentABundle']); + } + + /** + * @group legacy + */ + public function testInitializeBundlesSupportInheritanceCascade() + { + $grandparent = $this->getBundle(null, null, 'GrandParentBBundle'); + $parent = $this->getBundle(null, 'GrandParentBBundle', 'ParentBBundle'); + $child = $this->getBundle(null, 'ParentBBundle', 'ChildBBundle'); + + // use test kernel so we can access getBundleMap() + $kernel = $this->getKernelForTest(array('registerBundles')); + $kernel + ->expects($this->once()) + ->method('registerBundles') + ->will($this->returnValue(array($grandparent, $parent, $child))) + ; + $kernel->boot(); + + $map = $kernel->getBundleMap(); + $this->assertEquals(array($child, $parent, $grandparent), $map['GrandParentBBundle']); + $this->assertEquals(array($child, $parent), $map['ParentBBundle']); + $this->assertEquals(array($child), $map['ChildBBundle']); + } + + /** + * @group legacy + * @expectedException \LogicException + * @expectedExceptionMessage Bundle "ChildCBundle" extends bundle "FooBar", which is not registered. + */ + public function testInitializeBundlesThrowsExceptionWhenAParentDoesNotExists() + { + $child = $this->getBundle(null, 'FooBar', 'ChildCBundle'); + $kernel = $this->getKernel(array(), array($child)); + $kernel->boot(); + } + + /** + * @group legacy + */ + public function testInitializeBundlesSupportsArbitraryBundleRegistrationOrder() + { + $grandparent = $this->getBundle(null, null, 'GrandParentCBundle'); + $parent = $this->getBundle(null, 'GrandParentCBundle', 'ParentCBundle'); + $child = $this->getBundle(null, 'ParentCBundle', 'ChildCBundle'); + + // use test kernel so we can access getBundleMap() + $kernel = $this->getKernelForTest(array('registerBundles')); + $kernel + ->expects($this->once()) + ->method('registerBundles') + ->will($this->returnValue(array($parent, $grandparent, $child))) + ; + $kernel->boot(); + + $map = $kernel->getBundleMap(); + $this->assertEquals(array($child, $parent, $grandparent), $map['GrandParentCBundle']); + $this->assertEquals(array($child, $parent), $map['ParentCBundle']); + $this->assertEquals(array($child), $map['ChildCBundle']); + } + + /** + * @group legacy + * @expectedException \LogicException + * @expectedExceptionMessage Bundle "ParentCBundle" is directly extended by two bundles "ChildC2Bundle" and "ChildC1Bundle". + */ + public function testInitializeBundlesThrowsExceptionWhenABundleIsDirectlyExtendedByTwoBundles() + { + $parent = $this->getBundle(null, null, 'ParentCBundle'); + $child1 = $this->getBundle(null, 'ParentCBundle', 'ChildC1Bundle'); + $child2 = $this->getBundle(null, 'ParentCBundle', 'ChildC2Bundle'); + + $kernel = $this->getKernel(array(), array($parent, $child1, $child2)); + $kernel->boot(); + } + + /** + * @group legacy + * @expectedException \LogicException + * @expectedExceptionMessage Trying to register two bundles with the same name "DuplicateName" + */ + public function testInitializeBundleThrowsExceptionWhenRegisteringTwoBundlesWithTheSameName() + { + $fooBundle = $this->getBundle(null, null, 'FooBundle', 'DuplicateName'); + $barBundle = $this->getBundle(null, null, 'BarBundle', 'DuplicateName'); + + $kernel = $this->getKernel(array(), array($fooBundle, $barBundle)); + $kernel->boot(); + } + + /** + * @group legacy + * @expectedException \LogicException + * @expectedExceptionMessage Bundle "CircularRefBundle" can not extend itself. + */ + public function testInitializeBundleThrowsExceptionWhenABundleExtendsItself() + { + $circularRef = $this->getBundle(null, 'CircularRefBundle', 'CircularRefBundle'); + + $kernel = $this->getKernel(array(), array($circularRef)); + $kernel->boot(); + } + + public function testTerminateReturnsSilentlyIfKernelIsNotBooted() + { + $kernel = $this->getKernel(array('getHttpKernel')); + $kernel->expects($this->never()) + ->method('getHttpKernel'); + + $kernel->terminate(Request::create('/'), new Response()); + } + + public function testTerminateDelegatesTerminationOnlyForTerminableInterface() + { + // does not implement TerminableInterface + $httpKernel = new TestKernel(); + + $kernel = $this->getKernel(array('getHttpKernel')); + $kernel->expects($this->once()) + ->method('getHttpKernel') + ->willReturn($httpKernel); + + $kernel->boot(); + $kernel->terminate(Request::create('/'), new Response()); + + $this->assertFalse($httpKernel->terminateCalled, 'terminate() is never called if the kernel class does not implement TerminableInterface'); + + // implements TerminableInterface + $httpKernelMock = $this->getMockBuilder('Symfony\Component\HttpKernel\HttpKernel') + ->disableOriginalConstructor() + ->setMethods(array('terminate')) + ->getMock(); + + $httpKernelMock + ->expects($this->once()) + ->method('terminate'); + + $kernel = $this->getKernel(array('getHttpKernel')); + $kernel->expects($this->exactly(2)) + ->method('getHttpKernel') + ->will($this->returnValue($httpKernelMock)); + + $kernel->boot(); + $kernel->terminate(Request::create('/'), new Response()); + } + + public function testKernelWithoutBundles() + { + $kernel = new KernelWithoutBundles('test', true); + $kernel->boot(); + + $this->assertTrue($kernel->getContainer()->getParameter('test_executed')); + } + + public function testKernelRootDirNameStartingWithANumber() + { + $dir = __DIR__.'/Fixtures/123'; + require_once $dir.'/Kernel123.php'; + $kernel = new \Symfony\Component\HttpKernel\Tests\Fixtures\_123\Kernel123('dev', true); + $this->assertEquals('_123', $kernel->getName()); + } + + /** + * @group legacy + * @expectedDeprecation The Symfony\Component\HttpKernel\Kernel::getEnvParameters() method is deprecated as of 3.3 and will be removed in 4.0. Use the %cenv()%c syntax to get the value of any environment variable from configuration files instead. + * @expectedDeprecation The support of special environment variables that start with SYMFONY__ (such as "SYMFONY__FOO__BAR") is deprecated as of 3.3 and will be removed in 4.0. Use the %cenv()%c syntax instead to get the value of environment variables in configuration files. + */ + public function testSymfonyEnvironmentVariables() + { + $_SERVER['SYMFONY__FOO__BAR'] = 'baz'; + + $kernel = $this->getKernel(); + $method = new \ReflectionMethod($kernel, 'getEnvParameters'); + $method->setAccessible(true); + + $envParameters = $method->invoke($kernel); + $this->assertSame('baz', $envParameters['foo.bar']); + + unset($_SERVER['SYMFONY__FOO__BAR']); + } + + public function testProjectDirExtension() + { + $kernel = new CustomProjectDirKernel(); + $kernel->boot(); + + $this->assertSame('foo', $kernel->getProjectDir()); + $this->assertSame('foo', $kernel->getContainer()->getParameter('kernel.project_dir')); + } + + public function testKernelReset() + { + (new Filesystem())->remove(__DIR__.'/Fixtures/cache'); + + $kernel = new CustomProjectDirKernel(); + $kernel->boot(); + + $containerClass = get_class($kernel->getContainer()); + $containerFile = (new \ReflectionClass($kernel->getContainer()))->getFileName(); + unlink(__DIR__.'/Fixtures/cache/custom/FixturesCustomDebugProjectContainer.php.meta'); + + $kernel = new CustomProjectDirKernel(); + $kernel->boot(); + + $this->assertSame($containerClass, get_class($kernel->getContainer())); + $this->assertFileExists($containerFile); + unlink(__DIR__.'/Fixtures/cache/custom/FixturesCustomDebugProjectContainer.php.meta'); + + $kernel = new CustomProjectDirKernel(function ($container) { $container->register('foo', 'stdClass')->setPublic(true); }); + $kernel->boot(); + + $this->assertTrue(get_class($kernel->getContainer()) !== $containerClass); + $this->assertFileExists($containerFile); + $this->assertFileExists(dirname($containerFile).'.legacy'); + } + + public function testKernelPass() + { + $kernel = new PassKernel(); + $kernel->boot(); + + $this->assertTrue($kernel->getContainer()->getParameter('test.processed')); + } + + public function testServicesResetter() + { + $httpKernelMock = $this->getMockBuilder(HttpKernelInterface::class) + ->disableOriginalConstructor() + ->getMock(); + $httpKernelMock + ->expects($this->exactly(2)) + ->method('handle'); + + $kernel = new CustomProjectDirKernel(function ($container) { + $container->addCompilerPass(new ResettableServicePass()); + $container->register('one', ResettableService::class) + ->setPublic(true) + ->addTag('kernel.reset', array('method' => 'reset')); + $container->register('services_resetter', ServicesResetter::class)->setPublic(true); + }, $httpKernelMock, 'resetting'); + + ResettableService::$counter = 0; + + $request = new Request(); + + $kernel->handle($request); + $kernel->getContainer()->get('one'); + + $this->assertEquals(0, ResettableService::$counter); + $this->assertFalse($kernel->getContainer()->initialized('services_resetter')); + + $kernel->handle($request); + + $this->assertEquals(1, ResettableService::$counter); + } + + /** + * Returns a mock for the BundleInterface. + * + * @return BundleInterface + */ + protected function getBundle($dir = null, $parent = null, $className = null, $bundleName = null) + { + $bundle = $this + ->getMockBuilder('Symfony\Component\HttpKernel\Bundle\BundleInterface') + ->setMethods(array('getPath', 'getParent', 'getName')) + ->disableOriginalConstructor() + ; + + if ($className) { + $bundle->setMockClassName($className); + } + + $bundle = $bundle->getMockForAbstractClass(); + + $bundle + ->expects($this->any()) + ->method('getName') + ->will($this->returnValue(null === $bundleName ? get_class($bundle) : $bundleName)) + ; + + $bundle + ->expects($this->any()) + ->method('getPath') + ->will($this->returnValue($dir)) + ; + + $bundle + ->expects($this->any()) + ->method('getParent') + ->will($this->returnValue($parent)) + ; + + return $bundle; + } + + /** + * Returns a mock for the abstract kernel. + * + * @param array $methods Additional methods to mock (besides the abstract ones) + * @param array $bundles Bundles to register + * + * @return Kernel + */ + protected function getKernel(array $methods = array(), array $bundles = array()) + { + $methods[] = 'registerBundles'; + + $kernel = $this + ->getMockBuilder('Symfony\Component\HttpKernel\Kernel') + ->setMethods($methods) + ->setConstructorArgs(array('test', false)) + ->getMockForAbstractClass() + ; + $kernel->expects($this->any()) + ->method('registerBundles') + ->will($this->returnValue($bundles)) + ; + $p = new \ReflectionProperty($kernel, 'rootDir'); + $p->setAccessible(true); + $p->setValue($kernel, __DIR__.'/Fixtures'); + + return $kernel; + } + + protected function getKernelForTest(array $methods = array()) + { + $kernel = $this->getMockBuilder('Symfony\Component\HttpKernel\Tests\Fixtures\KernelForTest') + ->setConstructorArgs(array('test', false)) + ->setMethods($methods) + ->getMock(); + $p = new \ReflectionProperty($kernel, 'rootDir'); + $p->setAccessible(true); + $p->setValue($kernel, __DIR__.'/Fixtures'); + + return $kernel; + } +} + +class TestKernel implements HttpKernelInterface +{ + public $terminateCalled = false; + + public function terminate() + { + $this->terminateCalled = true; + } + + public function handle(Request $request, $type = self::MASTER_REQUEST, $catch = true) + { + } +} + +class CustomProjectDirKernel extends Kernel +{ + private $baseDir; + private $buildContainer; + private $httpKernel; + + public function __construct(\Closure $buildContainer = null, HttpKernelInterface $httpKernel = null, $name = 'custom') + { + parent::__construct($name, true); + + $this->baseDir = 'foo'; + $this->buildContainer = $buildContainer; + $this->httpKernel = $httpKernel; + } + + public function registerBundles() + { + return array(); + } + + public function registerContainerConfiguration(LoaderInterface $loader) + { + } + + public function getProjectDir() + { + return $this->baseDir; + } + + public function getRootDir() + { + return __DIR__.'/Fixtures'; + } + + protected function build(ContainerBuilder $container) + { + if ($build = $this->buildContainer) { + $build($container); + } + } + + protected function getHttpKernel() + { + return $this->httpKernel; + } +} + +class PassKernel extends CustomProjectDirKernel implements CompilerPassInterface +{ + public function __construct() + { + parent::__construct(); + Kernel::__construct('pass', true); + } + + public function process(ContainerBuilder $container) + { + $container->setParameter('test.processed', true); + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/Tests/Log/LoggerTest.php b/lib/silex/vendor/symfony/http-kernel/Tests/Log/LoggerTest.php new file mode 100644 index 000000000..ecf67af78 --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Tests/Log/LoggerTest.php @@ -0,0 +1,212 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Tests\Log; + +use PHPUnit\Framework\TestCase; +use Psr\Log\LoggerInterface; +use Psr\Log\LogLevel; +use Symfony\Component\HttpKernel\Log\Logger; + +/** + * @author Kévin Dunglas + * @author Jordi Boggiano + */ +class LoggerTest extends TestCase +{ + /** + * @var LoggerInterface + */ + private $logger; + + /** + * @var string + */ + private $tmpFile; + + protected function setUp() + { + $this->tmpFile = sys_get_temp_dir().DIRECTORY_SEPARATOR.'log'; + $this->logger = new Logger(LogLevel::DEBUG, $this->tmpFile); + } + + protected function tearDown() + { + if (!@unlink($this->tmpFile)) { + file_put_contents($this->tmpFile, ''); + } + } + + public static function assertLogsMatch(array $expected, array $given) + { + foreach ($given as $k => $line) { + self::assertThat(1 === preg_match('/[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}[\+-][0-9]{2}:[0-9]{2} '.preg_quote($expected[$k]).'/', $line), self::isTrue(), "\"$line\" do not match expected pattern \"$expected[$k]\""); + } + } + + /** + * Return the log messages in order. + * + * @return string[] + */ + public function getLogs() + { + return file($this->tmpFile, FILE_IGNORE_NEW_LINES); + } + + public function testImplements() + { + $this->assertInstanceOf(LoggerInterface::class, $this->logger); + } + + /** + * @dataProvider provideLevelsAndMessages + */ + public function testLogsAtAllLevels($level, $message) + { + $this->logger->{$level}($message, array('user' => 'Bob')); + $this->logger->log($level, $message, array('user' => 'Bob')); + + $expected = array( + "[$level] message of level $level with context: Bob", + "[$level] message of level $level with context: Bob", + ); + $this->assertLogsMatch($expected, $this->getLogs()); + } + + public function provideLevelsAndMessages() + { + return array( + LogLevel::EMERGENCY => array(LogLevel::EMERGENCY, 'message of level emergency with context: {user}'), + LogLevel::ALERT => array(LogLevel::ALERT, 'message of level alert with context: {user}'), + LogLevel::CRITICAL => array(LogLevel::CRITICAL, 'message of level critical with context: {user}'), + LogLevel::ERROR => array(LogLevel::ERROR, 'message of level error with context: {user}'), + LogLevel::WARNING => array(LogLevel::WARNING, 'message of level warning with context: {user}'), + LogLevel::NOTICE => array(LogLevel::NOTICE, 'message of level notice with context: {user}'), + LogLevel::INFO => array(LogLevel::INFO, 'message of level info with context: {user}'), + LogLevel::DEBUG => array(LogLevel::DEBUG, 'message of level debug with context: {user}'), + ); + } + + public function testLogLevelDisabled() + { + $this->logger = new Logger(LogLevel::INFO, $this->tmpFile); + + $this->logger->debug('test', array('user' => 'Bob')); + $this->logger->log(LogLevel::DEBUG, 'test', array('user' => 'Bob')); + + // Will always be true, but asserts than an exception isn't thrown + $this->assertSame(array(), $this->getLogs()); + } + + /** + * @expectedException \Psr\Log\InvalidArgumentException + */ + public function testThrowsOnInvalidLevel() + { + $this->logger->log('invalid level', 'Foo'); + } + + /** + * @expectedException \Psr\Log\InvalidArgumentException + */ + public function testThrowsOnInvalidMinLevel() + { + new Logger('invalid'); + } + + /** + * @expectedException \Psr\Log\InvalidArgumentException + */ + public function testInvalidOutput() + { + new Logger(LogLevel::DEBUG, '/'); + } + + public function testContextReplacement() + { + $logger = $this->logger; + $logger->info('{Message {nothing} {user} {foo.bar} a}', array('user' => 'Bob', 'foo.bar' => 'Bar')); + + $expected = array('[info] {Message {nothing} Bob Bar a}'); + $this->assertLogsMatch($expected, $this->getLogs()); + } + + public function testObjectCastToString() + { + if (method_exists($this, 'createPartialMock')) { + $dummy = $this->createPartialMock(DummyTest::class, array('__toString')); + } else { + $dummy = $this->getMock(DummyTest::class, array('__toString')); + } + $dummy->expects($this->atLeastOnce()) + ->method('__toString') + ->will($this->returnValue('DUMMY')); + + $this->logger->warning($dummy); + + $expected = array('[warning] DUMMY'); + $this->assertLogsMatch($expected, $this->getLogs()); + } + + public function testContextCanContainAnything() + { + $context = array( + 'bool' => true, + 'null' => null, + 'string' => 'Foo', + 'int' => 0, + 'float' => 0.5, + 'nested' => array('with object' => new DummyTest()), + 'object' => new \DateTime(), + 'resource' => fopen('php://memory', 'r'), + ); + + $this->logger->warning('Crazy context data', $context); + + $expected = array('[warning] Crazy context data'); + $this->assertLogsMatch($expected, $this->getLogs()); + } + + public function testContextExceptionKeyCanBeExceptionOrOtherValues() + { + $logger = $this->logger; + $logger->warning('Random message', array('exception' => 'oops')); + $logger->critical('Uncaught Exception!', array('exception' => new \LogicException('Fail'))); + + $expected = array( + '[warning] Random message', + '[critical] Uncaught Exception!', + ); + $this->assertLogsMatch($expected, $this->getLogs()); + } + + public function testFormatter() + { + $this->logger = new Logger(LogLevel::DEBUG, $this->tmpFile, function ($level, $message, $context) { + return json_encode(array('level' => $level, 'message' => $message, 'context' => $context)).\PHP_EOL; + }); + + $this->logger->error('An error', array('foo' => 'bar')); + $this->logger->warning('A warning', array('baz' => 'bar')); + $this->assertSame(array( + '{"level":"error","message":"An error","context":{"foo":"bar"}}', + '{"level":"warning","message":"A warning","context":{"baz":"bar"}}', + ), $this->getLogs()); + } +} + +class DummyTest +{ + public function __toString() + { + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/Tests/Logger.php b/lib/silex/vendor/symfony/http-kernel/Tests/Logger.php new file mode 100644 index 000000000..63c70bf67 --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Tests/Logger.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\HttpKernel\Tests; + +use Psr\Log\LoggerInterface; + +class Logger implements LoggerInterface +{ + protected $logs; + + public function __construct() + { + $this->clear(); + } + + public function getLogs($level = false) + { + return false === $level ? $this->logs : $this->logs[$level]; + } + + public function clear() + { + $this->logs = array( + 'emergency' => array(), + 'alert' => array(), + 'critical' => array(), + 'error' => array(), + 'warning' => array(), + 'notice' => array(), + 'info' => array(), + 'debug' => array(), + ); + } + + public function log($level, $message, array $context = array()) + { + $this->logs[$level][] = $message; + } + + public function emergency($message, array $context = array()) + { + $this->log('emergency', $message, $context); + } + + public function alert($message, array $context = array()) + { + $this->log('alert', $message, $context); + } + + public function critical($message, array $context = array()) + { + $this->log('critical', $message, $context); + } + + public function error($message, array $context = array()) + { + $this->log('error', $message, $context); + } + + public function warning($message, array $context = array()) + { + $this->log('warning', $message, $context); + } + + public function notice($message, array $context = array()) + { + $this->log('notice', $message, $context); + } + + public function info($message, array $context = array()) + { + $this->log('info', $message, $context); + } + + public function debug($message, array $context = array()) + { + $this->log('debug', $message, $context); + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/Tests/Profiler/FileProfilerStorageTest.php b/lib/silex/vendor/symfony/http-kernel/Tests/Profiler/FileProfilerStorageTest.php new file mode 100644 index 000000000..99ff20756 --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Tests/Profiler/FileProfilerStorageTest.php @@ -0,0 +1,350 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Tests\Profiler; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\HttpKernel\Profiler\FileProfilerStorage; +use Symfony\Component\HttpKernel\Profiler\Profile; + +class FileProfilerStorageTest extends TestCase +{ + private $tmpDir; + private $storage; + + protected function setUp() + { + $this->tmpDir = sys_get_temp_dir().'/sf2_profiler_file_storage'; + if (is_dir($this->tmpDir)) { + self::cleanDir(); + } + $this->storage = new FileProfilerStorage('file:'.$this->tmpDir); + $this->storage->purge(); + } + + protected function tearDown() + { + self::cleanDir(); + } + + public function testStore() + { + for ($i = 0; $i < 10; ++$i) { + $profile = new Profile('token_'.$i); + $profile->setIp('127.0.0.1'); + $profile->setUrl('http://foo.bar'); + $profile->setMethod('GET'); + $this->storage->write($profile); + } + $this->assertCount(10, $this->storage->find('127.0.0.1', 'http://foo.bar', 20, 'GET'), '->write() stores data in the storage'); + } + + public function testChildren() + { + $parentProfile = new Profile('token_parent'); + $parentProfile->setIp('127.0.0.1'); + $parentProfile->setUrl('http://foo.bar/parent'); + + $childProfile = new Profile('token_child'); + $childProfile->setIp('127.0.0.1'); + $childProfile->setUrl('http://foo.bar/child'); + + $parentProfile->addChild($childProfile); + + $this->storage->write($parentProfile); + $this->storage->write($childProfile); + + // Load them from storage + $parentProfile = $this->storage->read('token_parent'); + $childProfile = $this->storage->read('token_child'); + + // Check child has link to parent + $this->assertNotNull($childProfile->getParent()); + $this->assertEquals($parentProfile->getToken(), $childProfile->getParentToken()); + + // Check parent has child + $children = $parentProfile->getChildren(); + $this->assertCount(1, $children); + $this->assertEquals($childProfile->getToken(), $children[0]->getToken()); + } + + public function testStoreSpecialCharsInUrl() + { + // The storage accepts special characters in URLs (Even though URLs are not + // supposed to contain them) + $profile = new Profile('simple_quote'); + $profile->setUrl('http://foo.bar/\''); + $this->storage->write($profile); + $this->assertTrue(false !== $this->storage->read('simple_quote'), '->write() accepts single quotes in URL'); + + $profile = new Profile('double_quote'); + $profile->setUrl('http://foo.bar/"'); + $this->storage->write($profile); + $this->assertTrue(false !== $this->storage->read('double_quote'), '->write() accepts double quotes in URL'); + + $profile = new Profile('backslash'); + $profile->setUrl('http://foo.bar/\\'); + $this->storage->write($profile); + $this->assertTrue(false !== $this->storage->read('backslash'), '->write() accepts backslash in URL'); + + $profile = new Profile('comma'); + $profile->setUrl('http://foo.bar/,'); + $this->storage->write($profile); + $this->assertTrue(false !== $this->storage->read('comma'), '->write() accepts comma in URL'); + } + + public function testStoreDuplicateToken() + { + $profile = new Profile('token'); + $profile->setUrl('http://example.com/'); + + $this->assertTrue($this->storage->write($profile), '->write() returns true when the token is unique'); + + $profile->setUrl('http://example.net/'); + + $this->assertTrue($this->storage->write($profile), '->write() returns true when the token is already present in the storage'); + $this->assertEquals('http://example.net/', $this->storage->read('token')->getUrl(), '->write() overwrites the current profile data'); + + $this->assertCount(1, $this->storage->find('', '', 1000, ''), '->find() does not return the same profile twice'); + } + + public function testRetrieveByIp() + { + $profile = new Profile('token'); + $profile->setIp('127.0.0.1'); + $profile->setMethod('GET'); + $this->storage->write($profile); + + $this->assertCount(1, $this->storage->find('127.0.0.1', '', 10, 'GET'), '->find() retrieve a record by IP'); + $this->assertCount(0, $this->storage->find('127.0.%.1', '', 10, 'GET'), '->find() does not interpret a "%" as a wildcard in the IP'); + $this->assertCount(0, $this->storage->find('127.0._.1', '', 10, 'GET'), '->find() does not interpret a "_" as a wildcard in the IP'); + } + + public function testRetrieveByStatusCode() + { + $profile200 = new Profile('statuscode200'); + $profile200->setStatusCode(200); + $this->storage->write($profile200); + + $profile404 = new Profile('statuscode404'); + $profile404->setStatusCode(404); + $this->storage->write($profile404); + + $this->assertCount(1, $this->storage->find(null, null, 10, null, null, null, '200'), '->find() retrieve a record by Status code 200'); + $this->assertCount(1, $this->storage->find(null, null, 10, null, null, null, '404'), '->find() retrieve a record by Status code 404'); + } + + public function testRetrieveByUrl() + { + $profile = new Profile('simple_quote'); + $profile->setIp('127.0.0.1'); + $profile->setUrl('http://foo.bar/\''); + $profile->setMethod('GET'); + $this->storage->write($profile); + + $profile = new Profile('double_quote'); + $profile->setIp('127.0.0.1'); + $profile->setUrl('http://foo.bar/"'); + $profile->setMethod('GET'); + $this->storage->write($profile); + + $profile = new Profile('backslash'); + $profile->setIp('127.0.0.1'); + $profile->setUrl('http://foo\\bar/'); + $profile->setMethod('GET'); + $this->storage->write($profile); + + $profile = new Profile('percent'); + $profile->setIp('127.0.0.1'); + $profile->setUrl('http://foo.bar/%'); + $profile->setMethod('GET'); + $this->storage->write($profile); + + $profile = new Profile('underscore'); + $profile->setIp('127.0.0.1'); + $profile->setUrl('http://foo.bar/_'); + $profile->setMethod('GET'); + $this->storage->write($profile); + + $profile = new Profile('semicolon'); + $profile->setIp('127.0.0.1'); + $profile->setUrl('http://foo.bar/;'); + $profile->setMethod('GET'); + $this->storage->write($profile); + + $this->assertCount(1, $this->storage->find('127.0.0.1', 'http://foo.bar/\'', 10, 'GET'), '->find() accepts single quotes in URLs'); + $this->assertCount(1, $this->storage->find('127.0.0.1', 'http://foo.bar/"', 10, 'GET'), '->find() accepts double quotes in URLs'); + $this->assertCount(1, $this->storage->find('127.0.0.1', 'http://foo\\bar/', 10, 'GET'), '->find() accepts backslash in URLs'); + $this->assertCount(1, $this->storage->find('127.0.0.1', 'http://foo.bar/;', 10, 'GET'), '->find() accepts semicolon in URLs'); + $this->assertCount(1, $this->storage->find('127.0.0.1', 'http://foo.bar/%', 10, 'GET'), '->find() does not interpret a "%" as a wildcard in the URL'); + $this->assertCount(1, $this->storage->find('127.0.0.1', 'http://foo.bar/_', 10, 'GET'), '->find() does not interpret a "_" as a wildcard in the URL'); + } + + public function testStoreTime() + { + $dt = new \DateTime('now'); + $start = $dt->getTimestamp(); + + for ($i = 0; $i < 3; ++$i) { + $dt->modify('+1 minute'); + $profile = new Profile('time_'.$i); + $profile->setIp('127.0.0.1'); + $profile->setUrl('http://foo.bar'); + $profile->setTime($dt->getTimestamp()); + $profile->setMethod('GET'); + $this->storage->write($profile); + } + + $records = $this->storage->find('', '', 3, 'GET', $start, time() + 3 * 60); + $this->assertCount(3, $records, '->find() returns all previously added records'); + $this->assertEquals($records[0]['token'], 'time_2', '->find() returns records ordered by time in descendant order'); + $this->assertEquals($records[1]['token'], 'time_1', '->find() returns records ordered by time in descendant order'); + $this->assertEquals($records[2]['token'], 'time_0', '->find() returns records ordered by time in descendant order'); + + $records = $this->storage->find('', '', 3, 'GET', $start, time() + 2 * 60); + $this->assertCount(2, $records, '->find() should return only first two of the previously added records'); + } + + public function testRetrieveByEmptyUrlAndIp() + { + for ($i = 0; $i < 5; ++$i) { + $profile = new Profile('token_'.$i); + $profile->setMethod('GET'); + $this->storage->write($profile); + } + $this->assertCount(5, $this->storage->find('', '', 10, 'GET'), '->find() returns all previously added records'); + $this->storage->purge(); + } + + public function testRetrieveByMethodAndLimit() + { + foreach (array('POST', 'GET') as $method) { + for ($i = 0; $i < 5; ++$i) { + $profile = new Profile('token_'.$i.$method); + $profile->setMethod($method); + $this->storage->write($profile); + } + } + + $this->assertCount(5, $this->storage->find('', '', 5, 'POST')); + + $this->storage->purge(); + } + + public function testPurge() + { + $profile = new Profile('token1'); + $profile->setIp('127.0.0.1'); + $profile->setUrl('http://example.com/'); + $profile->setMethod('GET'); + $this->storage->write($profile); + + $this->assertTrue(false !== $this->storage->read('token1')); + $this->assertCount(1, $this->storage->find('127.0.0.1', '', 10, 'GET')); + + $profile = new Profile('token2'); + $profile->setIp('127.0.0.1'); + $profile->setUrl('http://example.net/'); + $profile->setMethod('GET'); + $this->storage->write($profile); + + $this->assertTrue(false !== $this->storage->read('token2')); + $this->assertCount(2, $this->storage->find('127.0.0.1', '', 10, 'GET')); + + $this->storage->purge(); + + $this->assertEmpty($this->storage->read('token'), '->purge() removes all data stored by profiler'); + $this->assertCount(0, $this->storage->find('127.0.0.1', '', 10, 'GET'), '->purge() removes all items from index'); + } + + public function testDuplicates() + { + for ($i = 1; $i <= 5; ++$i) { + $profile = new Profile('foo'.$i); + $profile->setIp('127.0.0.1'); + $profile->setUrl('http://example.net/'); + $profile->setMethod('GET'); + + ///three duplicates + $this->storage->write($profile); + $this->storage->write($profile); + $this->storage->write($profile); + } + $this->assertCount(3, $this->storage->find('127.0.0.1', 'http://example.net/', 3, 'GET'), '->find() method returns incorrect number of entries'); + } + + public function testStatusCode() + { + $profile = new Profile('token1'); + $profile->setStatusCode(200); + $this->storage->write($profile); + + $profile = new Profile('token2'); + $profile->setStatusCode(404); + $this->storage->write($profile); + + $tokens = $this->storage->find('', '', 10, ''); + $this->assertCount(2, $tokens); + $this->assertContains($tokens[0]['status_code'], array(200, 404)); + $this->assertContains($tokens[1]['status_code'], array(200, 404)); + } + + public function testMultiRowIndexFile() + { + $iteration = 3; + for ($i = 0; $i < $iteration; ++$i) { + $profile = new Profile('token'.$i); + $profile->setIp('127.0.0.'.$i); + $profile->setUrl('http://foo.bar/'.$i); + + $this->storage->write($profile); + $this->storage->write($profile); + $this->storage->write($profile); + } + + $handle = fopen($this->tmpDir.'/index.csv', 'r'); + for ($i = 0; $i < $iteration; ++$i) { + $row = fgetcsv($handle); + $this->assertEquals('token'.$i, $row[0]); + $this->assertEquals('127.0.0.'.$i, $row[1]); + $this->assertEquals('http://foo.bar/'.$i, $row[3]); + } + $this->assertFalse(fgetcsv($handle)); + } + + public function testReadLineFromFile() + { + $r = new \ReflectionMethod($this->storage, 'readLineFromFile'); + + $r->setAccessible(true); + + $h = tmpfile(); + + fwrite($h, "line1\n\n\nline2\n"); + fseek($h, 0, SEEK_END); + + $this->assertEquals('line2', $r->invoke($this->storage, $h)); + $this->assertEquals('line1', $r->invoke($this->storage, $h)); + } + + protected function cleanDir() + { + $flags = \FilesystemIterator::SKIP_DOTS; + $iterator = new \RecursiveDirectoryIterator($this->tmpDir, $flags); + $iterator = new \RecursiveIteratorIterator($iterator, \RecursiveIteratorIterator::SELF_FIRST); + + foreach ($iterator as $file) { + if (is_file($file)) { + unlink($file); + } + } + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/Tests/Profiler/ProfilerTest.php b/lib/silex/vendor/symfony/http-kernel/Tests/Profiler/ProfilerTest.php new file mode 100644 index 000000000..243c3c5c5 --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Tests/Profiler/ProfilerTest.php @@ -0,0 +1,105 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Tests\Profiler; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\HttpKernel\DataCollector\DataCollectorInterface; +use Symfony\Component\HttpKernel\DataCollector\RequestDataCollector; +use Symfony\Component\HttpKernel\Profiler\FileProfilerStorage; +use Symfony\Component\HttpKernel\Profiler\Profiler; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +class ProfilerTest extends TestCase +{ + private $tmp; + private $storage; + + public function testCollect() + { + $request = new Request(); + $request->query->set('foo', 'bar'); + $response = new Response('', 204); + $collector = new RequestDataCollector(); + + $profiler = new Profiler($this->storage); + $profiler->add($collector); + $profile = $profiler->collect($request, $response); + $profiler->saveProfile($profile); + + $this->assertSame(204, $profile->getStatusCode()); + $this->assertSame('GET', $profile->getMethod()); + $this->assertSame('bar', $profile->getCollector('request')->getRequestQuery()->all()['foo']->getValue()); + } + + public function testReset() + { + $collector = $this->getMockBuilder(DataCollectorInterface::class) + ->setMethods(['collect', 'getName', 'reset']) + ->getMock(); + $collector->expects($this->any())->method('getName')->willReturn('mock'); + $collector->expects($this->once())->method('reset'); + + $profiler = new Profiler($this->storage); + $profiler->add($collector); + $profiler->reset(); + } + + public function testFindWorksWithDates() + { + $profiler = new Profiler($this->storage); + + $this->assertCount(0, $profiler->find(null, null, null, null, '7th April 2014', '9th April 2014')); + } + + public function testFindWorksWithTimestamps() + { + $profiler = new Profiler($this->storage); + + $this->assertCount(0, $profiler->find(null, null, null, null, '1396828800', '1397001600')); + } + + public function testFindWorksWithInvalidDates() + { + $profiler = new Profiler($this->storage); + + $this->assertCount(0, $profiler->find(null, null, null, null, 'some string', '')); + } + + public function testFindWorksWithStatusCode() + { + $profiler = new Profiler($this->storage); + + $this->assertCount(0, $profiler->find(null, null, null, null, null, null, '204')); + } + + protected function setUp() + { + $this->tmp = tempnam(sys_get_temp_dir(), 'sf2_profiler'); + if (file_exists($this->tmp)) { + @unlink($this->tmp); + } + + $this->storage = new FileProfilerStorage('file:'.$this->tmp); + $this->storage->purge(); + } + + protected function tearDown() + { + if (null !== $this->storage) { + $this->storage->purge(); + $this->storage = null; + + @unlink($this->tmp); + } + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/Tests/TestHttpKernel.php b/lib/silex/vendor/symfony/http-kernel/Tests/TestHttpKernel.php new file mode 100644 index 000000000..3ec592725 --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Tests/TestHttpKernel.php @@ -0,0 +1,42 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Tests; + +use Symfony\Component\HttpKernel\Controller\ArgumentResolverInterface; +use Symfony\Component\HttpKernel\HttpKernel; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\Controller\ControllerResolverInterface; +use Symfony\Component\EventDispatcher\EventDispatcher; + +class TestHttpKernel extends HttpKernel implements ControllerResolverInterface, ArgumentResolverInterface +{ + public function __construct() + { + parent::__construct(new EventDispatcher(), $this, null, $this); + } + + public function getController(Request $request) + { + return array($this, 'callController'); + } + + public function getArguments(Request $request, $controller) + { + return array($request); + } + + public function callController(Request $request) + { + return new Response('Request: '.$request->getRequestUri()); + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/Tests/UriSignerTest.php b/lib/silex/vendor/symfony/http-kernel/Tests/UriSignerTest.php new file mode 100644 index 000000000..84ec19f70 --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/Tests/UriSignerTest.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\HttpKernel\Tests; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\HttpKernel\UriSigner; + +class UriSignerTest extends TestCase +{ + public function testSign() + { + $signer = new UriSigner('foobar'); + + $this->assertContains('?_hash=', $signer->sign('http://example.com/foo')); + $this->assertContains('&_hash=', $signer->sign('http://example.com/foo?foo=bar')); + } + + public function testCheck() + { + $signer = new UriSigner('foobar'); + + $this->assertFalse($signer->check('http://example.com/foo?_hash=foo')); + $this->assertFalse($signer->check('http://example.com/foo?foo=bar&_hash=foo')); + $this->assertFalse($signer->check('http://example.com/foo?foo=bar&_hash=foo&bar=foo')); + + $this->assertTrue($signer->check($signer->sign('http://example.com/foo'))); + $this->assertTrue($signer->check($signer->sign('http://example.com/foo?foo=bar'))); + $this->assertTrue($signer->check($signer->sign('http://example.com/foo?foo=bar&0=integer'))); + + $this->assertSame($signer->sign('http://example.com/foo?foo=bar&bar=foo'), $signer->sign('http://example.com/foo?bar=foo&foo=bar')); + } + + public function testCheckWithDifferentArgSeparator() + { + $this->iniSet('arg_separator.output', '&'); + $signer = new UriSigner('foobar'); + + $this->assertSame( + 'http://example.com/foo?baz=bay&foo=bar&_hash=rIOcC%2FF3DoEGo%2FvnESjSp7uU9zA9S%2F%2BOLhxgMexoPUM%3D', + $signer->sign('http://example.com/foo?foo=bar&baz=bay') + ); + $this->assertTrue($signer->check($signer->sign('http://example.com/foo?foo=bar&baz=bay'))); + } + + public function testCheckWithDifferentParameter() + { + $signer = new UriSigner('foobar', 'qux'); + + $this->assertSame( + 'http://example.com/foo?baz=bay&foo=bar&qux=rIOcC%2FF3DoEGo%2FvnESjSp7uU9zA9S%2F%2BOLhxgMexoPUM%3D', + $signer->sign('http://example.com/foo?foo=bar&baz=bay') + ); + $this->assertTrue($signer->check($signer->sign('http://example.com/foo?foo=bar&baz=bay'))); + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/UriSigner.php b/lib/silex/vendor/symfony/http-kernel/UriSigner.php new file mode 100644 index 000000000..28459b4ec --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/UriSigner.php @@ -0,0 +1,106 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel; + +/** + * Signs URIs. + * + * @author Fabien Potencier + */ +class UriSigner +{ + private $secret; + private $parameter; + + /** + * @param string $secret A secret + * @param string $parameter Query string parameter to use + */ + public function __construct($secret, $parameter = '_hash') + { + $this->secret = $secret; + $this->parameter = $parameter; + } + + /** + * Signs a URI. + * + * The given URI is signed by adding the query string parameter + * which value depends on the URI and the secret. + * + * @param string $uri A URI to sign + * + * @return string The signed URI + */ + public function sign($uri) + { + $url = parse_url($uri); + if (isset($url['query'])) { + parse_str($url['query'], $params); + } else { + $params = array(); + } + + $uri = $this->buildUrl($url, $params); + + return $uri.(false === strpos($uri, '?') ? '?' : '&').$this->parameter.'='.$this->computeHash($uri); + } + + /** + * Checks that a URI contains the correct hash. + * + * @param string $uri A signed URI + * + * @return bool True if the URI is signed correctly, false otherwise + */ + public function check($uri) + { + $url = parse_url($uri); + if (isset($url['query'])) { + parse_str($url['query'], $params); + } else { + $params = array(); + } + + if (empty($params[$this->parameter])) { + return false; + } + + $hash = urlencode($params[$this->parameter]); + unset($params[$this->parameter]); + + return $this->computeHash($this->buildUrl($url, $params)) === $hash; + } + + private function computeHash($uri) + { + return urlencode(base64_encode(hash_hmac('sha256', $uri, $this->secret, true))); + } + + private function buildUrl(array $url, array $params = array()) + { + ksort($params, SORT_STRING); + $url['query'] = http_build_query($params, '', '&'); + + $scheme = isset($url['scheme']) ? $url['scheme'].'://' : ''; + $host = isset($url['host']) ? $url['host'] : ''; + $port = isset($url['port']) ? ':'.$url['port'] : ''; + $user = isset($url['user']) ? $url['user'] : ''; + $pass = isset($url['pass']) ? ':'.$url['pass'] : ''; + $pass = ($user || $pass) ? "$pass@" : ''; + $path = isset($url['path']) ? $url['path'] : ''; + $query = isset($url['query']) && $url['query'] ? '?'.$url['query'] : ''; + $fragment = isset($url['fragment']) ? '#'.$url['fragment'] : ''; + + return $scheme.$user.$pass.$host.$port.$path.$query.$fragment; + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/composer.json b/lib/silex/vendor/symfony/http-kernel/composer.json new file mode 100644 index 000000000..b33675763 --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/composer.json @@ -0,0 +1,72 @@ +{ + "name": "symfony/http-kernel", + "type": "library", + "description": "Symfony HttpKernel Component", + "keywords": [], + "homepage": "https://symfony.com", + "license": "MIT", + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "require": { + "php": "^5.5.9|>=7.0.8", + "symfony/event-dispatcher": "~2.8|~3.0|~4.0", + "symfony/http-foundation": "^3.4.4|^4.0.4", + "symfony/debug": "~2.8|~3.0|~4.0", + "psr/log": "~1.0" + }, + "require-dev": { + "symfony/browser-kit": "~2.8|~3.0|~4.0", + "symfony/class-loader": "~2.8|~3.0", + "symfony/config": "~2.8|~3.0|~4.0", + "symfony/console": "~2.8|~3.0|~4.0", + "symfony/css-selector": "~2.8|~3.0|~4.0", + "symfony/dependency-injection": "~3.4|~4.0", + "symfony/dom-crawler": "~2.8|~3.0|~4.0", + "symfony/expression-language": "~2.8|~3.0|~4.0", + "symfony/finder": "~2.8|~3.0|~4.0", + "symfony/process": "~2.8|~3.0|~4.0", + "symfony/routing": "~3.4|~4.0", + "symfony/stopwatch": "~2.8|~3.0|~4.0", + "symfony/templating": "~2.8|~3.0|~4.0", + "symfony/translation": "~2.8|~3.0|~4.0", + "symfony/var-dumper": "~3.3|~4.0", + "psr/cache": "~1.0" + }, + "provide": { + "psr/log-implementation": "1.0" + }, + "conflict": { + "symfony/config": "<2.8", + "symfony/dependency-injection": "<3.4", + "symfony/var-dumper": "<3.3", + "twig/twig": "<1.34|<2.4,>=2" + }, + "suggest": { + "symfony/browser-kit": "", + "symfony/config": "", + "symfony/console": "", + "symfony/dependency-injection": "", + "symfony/finder": "", + "symfony/var-dumper": "" + }, + "autoload": { + "psr-4": { "Symfony\\Component\\HttpKernel\\": "" }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "minimum-stability": "dev", + "extra": { + "branch-alias": { + "dev-master": "3.4-dev" + } + } +} diff --git a/lib/silex/vendor/symfony/http-kernel/phpunit.xml.dist b/lib/silex/vendor/symfony/http-kernel/phpunit.xml.dist new file mode 100644 index 000000000..e0de769dd --- /dev/null +++ b/lib/silex/vendor/symfony/http-kernel/phpunit.xml.dist @@ -0,0 +1,40 @@ + + + + + + + + + + ./Tests/ + + + + + + ./ + + ./Tests + ./vendor + + + + + + + + + Symfony\Component\HttpFoundation + + + + + diff --git a/lib/silex/vendor/symfony/polyfill-mbstring/LICENSE b/lib/silex/vendor/symfony/polyfill-mbstring/LICENSE new file mode 100644 index 000000000..39fa189d2 --- /dev/null +++ b/lib/silex/vendor/symfony/polyfill-mbstring/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2014-2016 Fabien Potencier + +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/lib/silex/vendor/symfony/polyfill-mbstring/Mbstring.php b/lib/silex/vendor/symfony/polyfill-mbstring/Mbstring.php new file mode 100644 index 000000000..97e8c9b46 --- /dev/null +++ b/lib/silex/vendor/symfony/polyfill-mbstring/Mbstring.php @@ -0,0 +1,664 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Polyfill\Mbstring; + +/** + * Partial mbstring implementation in PHP, iconv based, UTF-8 centric. + * + * Implemented: + * - mb_chr - Returns a specific character from its Unicode code point + * - mb_convert_encoding - Convert character encoding + * - mb_convert_variables - Convert character code in variable(s) + * - mb_decode_mimeheader - Decode string in MIME header field + * - mb_encode_mimeheader - Encode string for MIME header XXX NATIVE IMPLEMENTATION IS REALLY BUGGED + * - mb_convert_case - Perform case folding on a string + * - mb_get_info - Get internal settings of mbstring + * - mb_http_input - Detect HTTP input character encoding + * - mb_http_output - Set/Get HTTP output character encoding + * - mb_internal_encoding - Set/Get internal character encoding + * - mb_list_encodings - Returns an array of all supported encodings + * - mb_ord - Returns the Unicode code point of a character + * - mb_output_handler - Callback function converts character encoding in output buffer + * - mb_scrub - Replaces ill-formed byte sequences with substitute characters + * - 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_strtolower - Make a string lowercase + * - mb_strtoupper - Make a string uppercase + * - mb_substitute_character - Set/Get substitution character + * - mb_substr - Get part of string + * - mb_stripos - Finds position of first occurrence of a string within another, case insensitive + * - mb_stristr - Finds first occurrence of a string within another, case insensitive + * - mb_strrchr - Finds the last occurrence of a character in a string within another + * - mb_strrichr - Finds the last occurrence of a character in a string within another, case insensitive + * - mb_strripos - Finds position of last occurrence of a string within another, case insensitive + * - mb_strstr - Finds first occurrence of a string within anothers + * - mb_strwidth - Return width of string + * - mb_substr_count - Count the number of substring occurrences + * + * Not implemented: + * - mb_convert_kana - Convert "kana" one from another ("zen-kaku", "han-kaku" and more) + * - mb_decode_numericentity - Decode HTML numeric string reference to character + * - mb_encode_numericentity - Encode character to HTML numeric string reference + * - mb_ereg_* - Regular expression with multibyte support + * - mb_parse_str - Parse GET/POST/COOKIE data and set global variable + * - mb_preferred_mime_name - Get MIME charset string + * - mb_regex_encoding - Returns current encoding for multibyte regex as string + * - mb_regex_set_options - Set/Get the default options for mbregex functions + * - mb_send_mail - Send encoded mail + * - mb_split - Split multibyte string using regular expression + * - mb_strcut - Get part of string + * - mb_strimwidth - Get truncated string with specified width + * + * @author Nicolas Grekas + * + * @internal + */ +final class Mbstring +{ + const MB_CASE_FOLD = PHP_INT_MAX; + + private static $encodingList = array('ASCII', 'UTF-8'); + private static $language = 'neutral'; + private static $internalEncoding = 'UTF-8'; + private static $caseFold = array( + array('µ','ſ',"\xCD\x85",'ς',"\xCF\x90","\xCF\x91","\xCF\x95","\xCF\x96","\xCF\xB0","\xCF\xB1","\xCF\xB5","\xE1\xBA\x9B","\xE1\xBE\xBE"), + array('μ','s','ι', 'σ','β', 'θ', 'φ', 'π', 'κ', 'ρ', 'ε', "\xE1\xB9\xA1",'ι'), + ); + + public static function mb_convert_encoding($s, $toEncoding, $fromEncoding = null) + { + if (is_array($fromEncoding) || false !== strpos($fromEncoding, ',')) { + $fromEncoding = self::mb_detect_encoding($s, $fromEncoding); + } else { + $fromEncoding = self::getEncoding($fromEncoding); + } + + $toEncoding = self::getEncoding($toEncoding); + + if ('BASE64' === $fromEncoding) { + $s = base64_decode($s); + $fromEncoding = $toEncoding; + } + + if ('BASE64' === $toEncoding) { + return base64_encode($s); + } + + if ('HTML-ENTITIES' === $toEncoding || 'HTML' === $toEncoding) { + if ('HTML-ENTITIES' === $fromEncoding || 'HTML' === $fromEncoding) { + $fromEncoding = 'Windows-1252'; + } + if ('UTF-8' !== $fromEncoding) { + $s = iconv($fromEncoding, 'UTF-8//IGNORE', $s); + } + + return preg_replace_callback('/[\x80-\xFF]+/', array(__CLASS__, 'html_encoding_callback'), $s); + } + + if ('HTML-ENTITIES' === $fromEncoding) { + $s = html_entity_decode($s, ENT_COMPAT, 'UTF-8'); + $fromEncoding = 'UTF-8'; + } + + return iconv($fromEncoding, $toEncoding.'//IGNORE', $s); + } + + public static function mb_convert_variables($toEncoding, $fromEncoding, &$a = null, &$b = null, &$c = null, &$d = null, &$e = null, &$f = null) + { + $vars = array(&$a, &$b, &$c, &$d, &$e, &$f); + + $ok = true; + array_walk_recursive($vars, function (&$v) use (&$ok, $toEncoding, $fromEncoding) { + if (false === $v = Mbstring::mb_convert_encoding($v, $toEncoding, $fromEncoding)) { + $ok = false; + } + }); + + return $ok ? $fromEncoding : false; + } + + public static function mb_decode_mimeheader($s) + { + return iconv_mime_decode($s, 2, self::$internalEncoding); + } + + public static function mb_encode_mimeheader($s, $charset = null, $transferEncoding = null, $linefeed = null, $indent = null) + { + trigger_error('mb_encode_mimeheader() is bugged. Please use iconv_mime_encode() instead', E_USER_WARNING); + } + + public static function mb_convert_case($s, $mode, $encoding = null) + { + if ('' === $s .= '') { + return ''; + } + + $encoding = self::getEncoding($encoding); + + if ('UTF-8' === $encoding) { + $encoding = null; + if (!preg_match('//u', $s)) { + $s = @iconv('UTF-8', 'UTF-8//IGNORE', $s); + } + } else { + $s = iconv($encoding, 'UTF-8//IGNORE', $s); + } + + if (MB_CASE_TITLE == $mode) { + $s = preg_replace_callback('/\b\p{Ll}/u', array(__CLASS__, 'title_case_upper'), $s); + $s = preg_replace_callback('/\B[\p{Lu}\p{Lt}]+/u', array(__CLASS__, 'title_case_lower'), $s); + } else { + if (MB_CASE_UPPER == $mode) { + static $upper = null; + if (null === $upper) { + $upper = self::getData('upperCase'); + } + $map = $upper; + } else { + if (self::MB_CASE_FOLD === $mode) { + $s = str_replace(self::$caseFold[0], self::$caseFold[1], $s); + } + + static $lower = null; + if (null === $lower) { + $lower = self::getData('lowerCase'); + } + $map = $lower; + } + + static $ulenMask = array("\xC0" => 2, "\xD0" => 2, "\xE0" => 3, "\xF0" => 4); + + $i = 0; + $len = strlen($s); + + while ($i < $len) { + $ulen = $s[$i] < "\x80" ? 1 : $ulenMask[$s[$i] & "\xF0"]; + $uchr = substr($s, $i, $ulen); + $i += $ulen; + + if (isset($map[$uchr])) { + $uchr = $map[$uchr]; + $nlen = strlen($uchr); + + if ($nlen == $ulen) { + $nlen = $i; + do { + $s[--$nlen] = $uchr[--$ulen]; + } while ($ulen); + } else { + $s = substr_replace($s, $uchr, $i - $ulen, $ulen); + $len += $nlen - $ulen; + $i += $nlen - $ulen; + } + } + } + } + + if (null === $encoding) { + return $s; + } + + return iconv('UTF-8', $encoding.'//IGNORE', $s); + } + + public static function mb_internal_encoding($encoding = null) + { + if (null === $encoding) { + return self::$internalEncoding; + } + + $encoding = self::getEncoding($encoding); + + if ('UTF-8' === $encoding || false !== @iconv($encoding, $encoding, ' ')) { + self::$internalEncoding = $encoding; + + return true; + } + + return false; + } + + public static function mb_language($lang = null) + { + if (null === $lang) { + return self::$language; + } + + switch ($lang = strtolower($lang)) { + case 'uni': + case 'neutral': + self::$language = $lang; + + return true; + } + + return false; + } + + public static function mb_list_encodings() + { + return array('UTF-8'); + } + + public static function mb_encoding_aliases($encoding) + { + switch (strtoupper($encoding)) { + case 'UTF8': + case 'UTF-8': + return array('utf8'); + } + + return false; + } + + public static function mb_check_encoding($var = null, $encoding = null) + { + if (null === $encoding) { + if (null === $var) { + return false; + } + $encoding = self::$internalEncoding; + } + + return self::mb_detect_encoding($var, array($encoding)) || false !== @iconv($encoding, $encoding, $var); + } + + public static function mb_detect_encoding($str, $encodingList = null, $strict = false) + { + if (null === $encodingList) { + $encodingList = self::$encodingList; + } else { + if (!is_array($encodingList)) { + $encodingList = array_map('trim', explode(',', $encodingList)); + } + $encodingList = array_map('strtoupper', $encodingList); + } + + foreach ($encodingList as $enc) { + switch ($enc) { + case 'ASCII': + if (!preg_match('/[\x80-\xFF]/', $str)) { + return $enc; + } + break; + + case 'UTF8': + case 'UTF-8': + if (preg_match('//u', $str)) { + return 'UTF-8'; + } + break; + + default: + if (0 === strncmp($enc, 'ISO-8859-', 9)) { + return $enc; + } + } + } + + return false; + } + + public static function mb_detect_order($encodingList = null) + { + if (null === $encodingList) { + return self::$encodingList; + } + + if (!is_array($encodingList)) { + $encodingList = array_map('trim', explode(',', $encodingList)); + } + $encodingList = array_map('strtoupper', $encodingList); + + foreach ($encodingList as $enc) { + switch ($enc) { + default: + if (strncmp($enc, 'ISO-8859-', 9)) { + return false; + } + case 'ASCII': + case 'UTF8': + case 'UTF-8': + } + } + + self::$encodingList = $encodingList; + + return true; + } + + public static function mb_strlen($s, $encoding = null) + { + $encoding = self::getEncoding($encoding); + if ('CP850' === $encoding || 'ASCII' === $encoding) { + return strlen($s); + } + + return @iconv_strlen($s, $encoding); + } + + public static function mb_strpos($haystack, $needle, $offset = 0, $encoding = null) + { + $encoding = self::getEncoding($encoding); + if ('CP850' === $encoding || 'ASCII' === $encoding) { + return strpos($haystack, $needle, $offset); + } + + if ('' === $needle .= '') { + trigger_error(__METHOD__.': Empty delimiter', E_USER_WARNING); + + return false; + } + + return iconv_strpos($haystack, $needle, $offset, $encoding); + } + + public static function mb_strrpos($haystack, $needle, $offset = 0, $encoding = null) + { + $encoding = self::getEncoding($encoding); + if ('CP850' === $encoding || 'ASCII' === $encoding) { + return strrpos($haystack, $needle, $offset); + } + + if ($offset != (int) $offset) { + $offset = 0; + } elseif ($offset = (int) $offset) { + if ($offset < 0) { + $haystack = self::mb_substr($haystack, 0, $offset, $encoding); + $offset = 0; + } else { + $haystack = self::mb_substr($haystack, $offset, 2147483647, $encoding); + } + } + + $pos = iconv_strrpos($haystack, $needle, $encoding); + + return false !== $pos ? $offset + $pos : false; + } + + public static function mb_strtolower($s, $encoding = null) + { + return self::mb_convert_case($s, MB_CASE_LOWER, $encoding); + } + + public static function mb_strtoupper($s, $encoding = null) + { + return self::mb_convert_case($s, MB_CASE_UPPER, $encoding); + } + + public static function mb_substitute_character($c = null) + { + if (0 === strcasecmp($c, 'none')) { + return true; + } + + return null !== $c ? false : 'none'; + } + + 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); + } + + if ($start < 0) { + $start = iconv_strlen($s, $encoding) + $start; + if ($start < 0) { + $start = 0; + } + } + + if (null === $length) { + $length = 2147483647; + } elseif ($length < 0) { + $length = iconv_strlen($s, $encoding) + $length - $start; + if ($length < 0) { + return ''; + } + } + + return iconv_substr($s, $start, $length, $encoding).''; + } + + public static function mb_stripos($haystack, $needle, $offset = 0, $encoding = null) + { + $haystack = self::mb_convert_case($haystack, self::MB_CASE_FOLD, $encoding); + $needle = self::mb_convert_case($needle, self::MB_CASE_FOLD, $encoding); + + return self::mb_strpos($haystack, $needle, $offset, $encoding); + } + + public static function mb_stristr($haystack, $needle, $part = false, $encoding = null) + { + $pos = self::mb_stripos($haystack, $needle, 0, $encoding); + + return self::getSubpart($pos, $part, $haystack, $encoding); + } + + public static function mb_strrchr($haystack, $needle, $part = false, $encoding = null) + { + $encoding = self::getEncoding($encoding); + if ('CP850' === $encoding || 'ASCII' === $encoding) { + return strrchr($haystack, $needle, $part); + } + $needle = self::mb_substr($needle, 0, 1, $encoding); + $pos = iconv_strrpos($haystack, $needle, $encoding); + + return self::getSubpart($pos, $part, $haystack, $encoding); + } + + public static function mb_strrichr($haystack, $needle, $part = false, $encoding = null) + { + $needle = self::mb_substr($needle, 0, 1, $encoding); + $pos = self::mb_strripos($haystack, $needle, $encoding); + + return self::getSubpart($pos, $part, $haystack, $encoding); + } + + public static function mb_strripos($haystack, $needle, $offset = 0, $encoding = null) + { + $haystack = self::mb_convert_case($haystack, self::MB_CASE_FOLD, $encoding); + $needle = self::mb_convert_case($needle, self::MB_CASE_FOLD, $encoding); + + return self::mb_strrpos($haystack, $needle, $offset, $encoding); + } + + public static function mb_strstr($haystack, $needle, $part = false, $encoding = null) + { + $pos = strpos($haystack, $needle); + if (false === $pos) { + return false; + } + if ($part) { + return substr($haystack, 0, $pos); + } + + return substr($haystack, $pos); + } + + public static function mb_get_info($type = 'all') + { + $info = array( + 'internal_encoding' => self::$internalEncoding, + 'http_output' => 'pass', + 'http_output_conv_mimetypes' => '^(text/|application/xhtml\+xml)', + 'func_overload' => 0, + 'func_overload_list' => 'no overload', + 'mail_charset' => 'UTF-8', + 'mail_header_encoding' => 'BASE64', + 'mail_body_encoding' => 'BASE64', + 'illegal_chars' => 0, + 'encoding_translation' => 'Off', + 'language' => self::$language, + 'detect_order' => self::$encodingList, + 'substitute_character' => 'none', + 'strict_detection' => 'Off', + ); + + if ('all' === $type) { + return $info; + } + if (isset($info[$type])) { + return $info[$type]; + } + + return false; + } + + public static function mb_http_input($type = '') + { + return false; + } + + public static function mb_http_output($encoding = null) + { + return null !== $encoding ? 'pass' === $encoding : 'pass'; + } + + public static function mb_strwidth($s, $encoding = null) + { + $encoding = self::getEncoding($encoding); + + if ('UTF-8' !== $encoding) { + $s = iconv($encoding, 'UTF-8//IGNORE', $s); + } + + $s = preg_replace('/[\x{1100}-\x{115F}\x{2329}\x{232A}\x{2E80}-\x{303E}\x{3040}-\x{A4CF}\x{AC00}-\x{D7A3}\x{F900}-\x{FAFF}\x{FE10}-\x{FE19}\x{FE30}-\x{FE6F}\x{FF00}-\x{FF60}\x{FFE0}-\x{FFE6}\x{20000}-\x{2FFFD}\x{30000}-\x{3FFFD}]/u', '', $s, -1, $wide); + + return ($wide << 1) + iconv_strlen($s, 'UTF-8'); + } + + public static function mb_substr_count($haystack, $needle, $encoding = null) + { + return substr_count($haystack, $needle); + } + + public static function mb_output_handler($contents, $status) + { + return $contents; + } + + 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 = self::getEncoding($encoding)) { + $s = mb_convert_encoding($s, $encoding, 'UTF-8'); + } + + return $s; + } + + public static function mb_ord($s, $encoding = null) + { + if ('UTF-8' !== $encoding = self::getEncoding($encoding)) { + $s = mb_convert_encoding($s, 'UTF-8', $encoding); + } + + $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; + } + + private static function getSubpart($pos, $part, $haystack, $encoding) + { + if (false === $pos) { + return false; + } + if ($part) { + return self::mb_substr($haystack, 0, $pos, $encoding); + } + + return self::mb_substr($haystack, $pos, null, $encoding); + } + + private static function html_encoding_callback($m) + { + $i = 1; + $entities = ''; + $m = unpack('C*', htmlentities($m[0], ENT_COMPAT, 'UTF-8')); + + while (isset($m[$i])) { + if (0x80 > $m[$i]) { + $entities .= chr($m[$i++]); + continue; + } + if (0xF0 <= $m[$i]) { + $c = (($m[$i++] - 0xF0) << 18) + (($m[$i++] - 0x80) << 12) + (($m[$i++] - 0x80) << 6) + $m[$i++] - 0x80; + } elseif (0xE0 <= $m[$i]) { + $c = (($m[$i++] - 0xE0) << 12) + (($m[$i++] - 0x80) << 6) + $m[$i++] - 0x80; + } else { + $c = (($m[$i++] - 0xC0) << 6) + $m[$i++] - 0x80; + } + + $entities .= '&#'.$c.';'; + } + + return $entities; + } + + private static function title_case_lower($s) + { + return self::mb_convert_case($s[0], MB_CASE_LOWER, 'UTF-8'); + } + + private static function title_case_upper($s) + { + return self::mb_convert_case($s[0], MB_CASE_UPPER, 'UTF-8'); + } + + private static function getData($file) + { + if (file_exists($file = __DIR__.'/Resources/unidata/'.$file.'.php')) { + return require $file; + } + + return false; + } + + private static function getEncoding($encoding) + { + if (null === $encoding) { + return self::$internalEncoding; + } + + $encoding = strtoupper($encoding); + + if ('8BIT' === $encoding || 'BINARY' === $encoding) { + return 'CP850'; + } + if ('UTF8' === $encoding) { + return 'UTF-8'; + } + + return $encoding; + } +} diff --git a/lib/silex/vendor/symfony/polyfill-mbstring/README.md b/lib/silex/vendor/symfony/polyfill-mbstring/README.md new file mode 100644 index 000000000..342e8286d --- /dev/null +++ b/lib/silex/vendor/symfony/polyfill-mbstring/README.md @@ -0,0 +1,13 @@ +Symfony Polyfill / Mbstring +=========================== + +This component provides a partial, native PHP implementation for the +[Mbstring](http://php.net/mbstring) 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/lib/silex/vendor/symfony/polyfill-mbstring/Resources/unidata/lowerCase.php b/lib/silex/vendor/symfony/polyfill-mbstring/Resources/unidata/lowerCase.php new file mode 100644 index 000000000..3ca16416a --- /dev/null +++ b/lib/silex/vendor/symfony/polyfill-mbstring/Resources/unidata/lowerCase.php @@ -0,0 +1,1101 @@ + 'a', + 'B' => 'b', + 'C' => 'c', + 'D' => 'd', + 'E' => 'e', + 'F' => 'f', + 'G' => 'g', + 'H' => 'h', + 'I' => 'i', + 'J' => 'j', + 'K' => 'k', + 'L' => 'l', + 'M' => 'm', + 'N' => 'n', + 'O' => 'o', + 'P' => 'p', + 'Q' => 'q', + 'R' => 'r', + 'S' => 's', + 'T' => 't', + 'U' => 'u', + 'V' => 'v', + 'W' => 'w', + 'X' => 'x', + 'Y' => 'y', + 'Z' => 'z', + 'À' => 'à', + 'Á' => 'á', + 'Â' => 'â', + 'Ã' => 'ã', + 'Ä' => 'ä', + 'Å' => 'å', + 'Æ' => 'æ', + 'Ç' => 'ç', + 'È' => 'è', + 'É' => 'é', + 'Ê' => 'ê', + 'Ë' => 'ë', + 'Ì' => 'ì', + 'Í' => 'í', + 'Î' => 'î', + 'Ï' => 'ï', + 'Ð' => 'ð', + 'Ñ' => 'ñ', + 'Ò' => 'ò', + 'Ó' => 'ó', + 'Ô' => 'ô', + 'Õ' => 'õ', + 'Ö' => 'ö', + 'Ø' => 'ø', + 'Ù' => 'ù', + 'Ú' => 'ú', + 'Û' => 'û', + 'Ü' => 'ü', + 'Ý' => 'ý', + 'Þ' => 'þ', + 'Ā' => 'ā', + 'Ă' => 'ă', + 'Ą' => 'ą', + 'Ć' => 'ć', + 'Ĉ' => 'ĉ', + 'Ċ' => 'ċ', + 'Č' => 'č', + 'Ď' => 'ď', + 'Đ' => 'đ', + 'Ē' => 'ē', + 'Ĕ' => 'ĕ', + 'Ė' => 'ė', + 'Ę' => 'ę', + 'Ě' => 'ě', + 'Ĝ' => 'ĝ', + 'Ğ' => 'ğ', + 'Ġ' => 'ġ', + 'Ģ' => 'ģ', + 'Ĥ' => 'ĥ', + 'Ħ' => 'ħ', + 'Ĩ' => 'ĩ', + 'Ī' => 'ī', + 'Ĭ' => 'ĭ', + 'Į' => 'į', + 'İ' => 'i', + 'IJ' => 'ij', + 'Ĵ' => 'ĵ', + 'Ķ' => 'ķ', + 'Ĺ' => 'ĺ', + 'Ļ' => 'ļ', + 'Ľ' => 'ľ', + 'Ŀ' => 'ŀ', + 'Ł' => 'ł', + 'Ń' => 'ń', + 'Ņ' => 'ņ', + 'Ň' => 'ň', + 'Ŋ' => 'ŋ', + 'Ō' => 'ō', + 'Ŏ' => 'ŏ', + 'Ő' => 'ő', + 'Œ' => 'œ', + 'Ŕ' => 'ŕ', + 'Ŗ' => 'ŗ', + 'Ř' => 'ř', + 'Ś' => 'ś', + 'Ŝ' => 'ŝ', + 'Ş' => 'ş', + 'Š' => 'š', + 'Ţ' => 'ţ', + 'Ť' => 'ť', + 'Ŧ' => 'ŧ', + 'Ũ' => 'ũ', + 'Ū' => 'ū', + 'Ŭ' => 'ŭ', + 'Ů' => 'ů', + 'Ű' => 'ű', + 'Ų' => 'ų', + 'Ŵ' => 'ŵ', + 'Ŷ' => 'ŷ', + 'Ÿ' => 'ÿ', + 'Ź' => 'ź', + 'Ż' => 'ż', + 'Ž' => 'ž', + 'Ɓ' => 'ɓ', + 'Ƃ' => 'ƃ', + 'Ƅ' => 'ƅ', + 'Ɔ' => 'ɔ', + 'Ƈ' => 'ƈ', + 'Ɖ' => 'ɖ', + 'Ɗ' => 'ɗ', + 'Ƌ' => 'ƌ', + 'Ǝ' => 'ǝ', + 'Ə' => 'ə', + 'Ɛ' => 'ɛ', + 'Ƒ' => 'ƒ', + 'Ɠ' => 'ɠ', + 'Ɣ' => 'ɣ', + 'Ɩ' => 'ɩ', + 'Ɨ' => 'ɨ', + 'Ƙ' => 'ƙ', + 'Ɯ' => 'ɯ', + 'Ɲ' => 'ɲ', + 'Ɵ' => 'ɵ', + 'Ơ' => 'ơ', + 'Ƣ' => 'ƣ', + 'Ƥ' => 'ƥ', + 'Ʀ' => 'ʀ', + 'Ƨ' => 'ƨ', + 'Ʃ' => 'ʃ', + 'Ƭ' => 'ƭ', + 'Ʈ' => 'ʈ', + 'Ư' => 'ư', + 'Ʊ' => 'ʊ', + 'Ʋ' => 'ʋ', + 'Ƴ' => 'ƴ', + 'Ƶ' => 'ƶ', + 'Ʒ' => 'ʒ', + 'Ƹ' => 'ƹ', + 'Ƽ' => 'ƽ', + 'DŽ' => 'dž', + 'Dž' => 'dž', + 'LJ' => 'lj', + 'Lj' => 'lj', + 'NJ' => 'nj', + 'Nj' => 'nj', + 'Ǎ' => 'ǎ', + 'Ǐ' => 'ǐ', + 'Ǒ' => 'ǒ', + 'Ǔ' => 'ǔ', + 'Ǖ' => 'ǖ', + 'Ǘ' => 'ǘ', + 'Ǚ' => 'ǚ', + 'Ǜ' => 'ǜ', + 'Ǟ' => 'ǟ', + 'Ǡ' => 'ǡ', + 'Ǣ' => 'ǣ', + 'Ǥ' => 'ǥ', + 'Ǧ' => 'ǧ', + 'Ǩ' => 'ǩ', + 'Ǫ' => 'ǫ', + 'Ǭ' => 'ǭ', + 'Ǯ' => 'ǯ', + 'DZ' => 'dz', + 'Dz' => 'dz', + 'Ǵ' => 'ǵ', + 'Ƕ' => 'ƕ', + 'Ƿ' => 'ƿ', + 'Ǹ' => 'ǹ', + 'Ǻ' => 'ǻ', + 'Ǽ' => 'ǽ', + 'Ǿ' => 'ǿ', + 'Ȁ' => 'ȁ', + 'Ȃ' => 'ȃ', + 'Ȅ' => 'ȅ', + 'Ȇ' => 'ȇ', + 'Ȉ' => 'ȉ', + 'Ȋ' => 'ȋ', + 'Ȍ' => 'ȍ', + 'Ȏ' => 'ȏ', + 'Ȑ' => 'ȑ', + 'Ȓ' => 'ȓ', + 'Ȕ' => 'ȕ', + 'Ȗ' => 'ȗ', + 'Ș' => 'ș', + 'Ț' => 'ț', + 'Ȝ' => 'ȝ', + 'Ȟ' => 'ȟ', + 'Ƞ' => 'ƞ', + 'Ȣ' => 'ȣ', + 'Ȥ' => 'ȥ', + 'Ȧ' => 'ȧ', + 'Ȩ' => 'ȩ', + 'Ȫ' => 'ȫ', + 'Ȭ' => 'ȭ', + 'Ȯ' => 'ȯ', + 'Ȱ' => 'ȱ', + 'Ȳ' => 'ȳ', + 'Ⱥ' => 'ⱥ', + 'Ȼ' => 'ȼ', + 'Ƚ' => 'ƚ', + 'Ⱦ' => 'ⱦ', + 'Ɂ' => 'ɂ', + 'Ƀ' => 'ƀ', + 'Ʉ' => 'ʉ', + 'Ʌ' => 'ʌ', + 'Ɇ' => 'ɇ', + 'Ɉ' => 'ɉ', + 'Ɋ' => 'ɋ', + 'Ɍ' => 'ɍ', + 'Ɏ' => 'ɏ', + 'Ͱ' => 'ͱ', + 'Ͳ' => 'ͳ', + 'Ͷ' => 'ͷ', + 'Ϳ' => 'ϳ', + 'Ά' => 'ά', + 'Έ' => 'έ', + 'Ή' => 'ή', + 'Ί' => 'ί', + 'Ό' => 'ό', + 'Ύ' => 'ύ', + 'Ώ' => 'ώ', + 'Α' => 'α', + 'Β' => 'β', + 'Γ' => 'γ', + 'Δ' => 'δ', + 'Ε' => 'ε', + 'Ζ' => 'ζ', + 'Η' => 'η', + 'Θ' => 'θ', + 'Ι' => 'ι', + 'Κ' => 'κ', + 'Λ' => 'λ', + 'Μ' => 'μ', + 'Ν' => 'ν', + 'Ξ' => 'ξ', + 'Ο' => 'ο', + 'Π' => 'π', + 'Ρ' => 'ρ', + 'Σ' => 'σ', + 'Τ' => 'τ', + 'Υ' => 'υ', + 'Φ' => 'φ', + 'Χ' => 'χ', + 'Ψ' => 'ψ', + 'Ω' => 'ω', + 'Ϊ' => 'ϊ', + 'Ϋ' => 'ϋ', + 'Ϗ' => 'ϗ', + 'Ϙ' => 'ϙ', + 'Ϛ' => 'ϛ', + 'Ϝ' => 'ϝ', + 'Ϟ' => 'ϟ', + 'Ϡ' => 'ϡ', + 'Ϣ' => 'ϣ', + 'Ϥ' => 'ϥ', + 'Ϧ' => 'ϧ', + 'Ϩ' => 'ϩ', + 'Ϫ' => 'ϫ', + 'Ϭ' => 'ϭ', + 'Ϯ' => 'ϯ', + 'ϴ' => 'θ', + 'Ϸ' => 'ϸ', + 'Ϲ' => 'ϲ', + 'Ϻ' => 'ϻ', + 'Ͻ' => 'ͻ', + 'Ͼ' => 'ͼ', + 'Ͽ' => 'ͽ', + 'Ѐ' => 'ѐ', + 'Ё' => 'ё', + 'Ђ' => 'ђ', + 'Ѓ' => 'ѓ', + 'Є' => 'є', + 'Ѕ' => 'ѕ', + 'І' => 'і', + 'Ї' => 'ї', + 'Ј' => 'ј', + 'Љ' => 'љ', + 'Њ' => 'њ', + 'Ћ' => 'ћ', + 'Ќ' => 'ќ', + 'Ѝ' => 'ѝ', + 'Ў' => 'ў', + 'Џ' => 'џ', + 'А' => 'а', + 'Б' => 'б', + 'В' => 'в', + 'Г' => 'г', + 'Д' => 'д', + 'Е' => 'е', + 'Ж' => 'ж', + 'З' => 'з', + 'И' => 'и', + 'Й' => 'й', + 'К' => 'к', + 'Л' => 'л', + 'М' => 'м', + 'Н' => 'н', + 'О' => 'о', + 'П' => 'п', + 'Р' => 'р', + 'С' => 'с', + 'Т' => 'т', + 'У' => 'у', + 'Ф' => 'ф', + 'Х' => 'х', + 'Ц' => 'ц', + 'Ч' => 'ч', + 'Ш' => 'ш', + 'Щ' => 'щ', + 'Ъ' => 'ъ', + 'Ы' => 'ы', + 'Ь' => 'ь', + 'Э' => 'э', + 'Ю' => 'ю', + 'Я' => 'я', + 'Ѡ' => 'ѡ', + 'Ѣ' => 'ѣ', + 'Ѥ' => 'ѥ', + 'Ѧ' => 'ѧ', + 'Ѩ' => 'ѩ', + 'Ѫ' => 'ѫ', + 'Ѭ' => 'ѭ', + 'Ѯ' => 'ѯ', + 'Ѱ' => 'ѱ', + 'Ѳ' => 'ѳ', + 'Ѵ' => 'ѵ', + 'Ѷ' => 'ѷ', + 'Ѹ' => 'ѹ', + 'Ѻ' => 'ѻ', + 'Ѽ' => 'ѽ', + 'Ѿ' => 'ѿ', + 'Ҁ' => 'ҁ', + 'Ҋ' => 'ҋ', + 'Ҍ' => 'ҍ', + 'Ҏ' => 'ҏ', + 'Ґ' => 'ґ', + 'Ғ' => 'ғ', + 'Ҕ' => 'ҕ', + 'Җ' => 'җ', + 'Ҙ' => 'ҙ', + 'Қ' => 'қ', + 'Ҝ' => 'ҝ', + 'Ҟ' => 'ҟ', + 'Ҡ' => 'ҡ', + 'Ң' => 'ң', + 'Ҥ' => 'ҥ', + 'Ҧ' => 'ҧ', + 'Ҩ' => 'ҩ', + 'Ҫ' => 'ҫ', + 'Ҭ' => 'ҭ', + 'Ү' => 'ү', + 'Ұ' => 'ұ', + 'Ҳ' => 'ҳ', + 'Ҵ' => 'ҵ', + 'Ҷ' => 'ҷ', + 'Ҹ' => 'ҹ', + 'Һ' => 'һ', + 'Ҽ' => 'ҽ', + 'Ҿ' => 'ҿ', + 'Ӏ' => 'ӏ', + 'Ӂ' => 'ӂ', + 'Ӄ' => 'ӄ', + 'Ӆ' => 'ӆ', + 'Ӈ' => 'ӈ', + 'Ӊ' => 'ӊ', + 'Ӌ' => 'ӌ', + 'Ӎ' => 'ӎ', + 'Ӑ' => 'ӑ', + 'Ӓ' => 'ӓ', + 'Ӕ' => 'ӕ', + 'Ӗ' => 'ӗ', + 'Ә' => 'ә', + 'Ӛ' => 'ӛ', + 'Ӝ' => 'ӝ', + 'Ӟ' => 'ӟ', + 'Ӡ' => 'ӡ', + 'Ӣ' => 'ӣ', + 'Ӥ' => 'ӥ', + 'Ӧ' => 'ӧ', + 'Ө' => 'ө', + 'Ӫ' => 'ӫ', + 'Ӭ' => 'ӭ', + 'Ӯ' => 'ӯ', + 'Ӱ' => 'ӱ', + 'Ӳ' => 'ӳ', + 'Ӵ' => 'ӵ', + 'Ӷ' => 'ӷ', + 'Ӹ' => 'ӹ', + 'Ӻ' => 'ӻ', + 'Ӽ' => 'ӽ', + 'Ӿ' => 'ӿ', + 'Ԁ' => 'ԁ', + 'Ԃ' => 'ԃ', + 'Ԅ' => 'ԅ', + 'Ԇ' => 'ԇ', + 'Ԉ' => 'ԉ', + 'Ԋ' => 'ԋ', + 'Ԍ' => 'ԍ', + 'Ԏ' => 'ԏ', + 'Ԑ' => 'ԑ', + 'Ԓ' => 'ԓ', + 'Ԕ' => 'ԕ', + 'Ԗ' => 'ԗ', + 'Ԙ' => 'ԙ', + 'Ԛ' => 'ԛ', + 'Ԝ' => 'ԝ', + 'Ԟ' => 'ԟ', + 'Ԡ' => 'ԡ', + 'Ԣ' => 'ԣ', + 'Ԥ' => 'ԥ', + 'Ԧ' => 'ԧ', + 'Ԩ' => 'ԩ', + 'Ԫ' => 'ԫ', + 'Ԭ' => 'ԭ', + 'Ԯ' => 'ԯ', + 'Ա' => 'ա', + 'Բ' => 'բ', + 'Գ' => 'գ', + 'Դ' => 'դ', + 'Ե' => 'ե', + 'Զ' => 'զ', + 'Է' => 'է', + 'Ը' => 'ը', + 'Թ' => 'թ', + 'Ժ' => 'ժ', + 'Ի' => 'ի', + 'Լ' => 'լ', + 'Խ' => 'խ', + 'Ծ' => 'ծ', + 'Կ' => 'կ', + 'Հ' => 'հ', + 'Ձ' => 'ձ', + 'Ղ' => 'ղ', + 'Ճ' => 'ճ', + 'Մ' => 'մ', + 'Յ' => 'յ', + 'Ն' => 'ն', + 'Շ' => 'շ', + 'Ո' => 'ո', + 'Չ' => 'չ', + 'Պ' => 'պ', + 'Ջ' => 'ջ', + 'Ռ' => 'ռ', + 'Ս' => 'ս', + 'Վ' => 'վ', + 'Տ' => 'տ', + 'Ր' => 'ր', + 'Ց' => 'ց', + 'Ւ' => 'ւ', + 'Փ' => 'փ', + 'Ք' => 'ք', + 'Օ' => 'օ', + 'Ֆ' => 'ֆ', + 'Ⴀ' => 'ⴀ', + 'Ⴁ' => 'ⴁ', + 'Ⴂ' => 'ⴂ', + 'Ⴃ' => 'ⴃ', + 'Ⴄ' => 'ⴄ', + 'Ⴅ' => 'ⴅ', + 'Ⴆ' => 'ⴆ', + 'Ⴇ' => 'ⴇ', + 'Ⴈ' => 'ⴈ', + 'Ⴉ' => 'ⴉ', + 'Ⴊ' => 'ⴊ', + 'Ⴋ' => 'ⴋ', + 'Ⴌ' => 'ⴌ', + 'Ⴍ' => 'ⴍ', + 'Ⴎ' => 'ⴎ', + 'Ⴏ' => 'ⴏ', + 'Ⴐ' => 'ⴐ', + 'Ⴑ' => 'ⴑ', + 'Ⴒ' => 'ⴒ', + 'Ⴓ' => 'ⴓ', + 'Ⴔ' => 'ⴔ', + 'Ⴕ' => 'ⴕ', + 'Ⴖ' => 'ⴖ', + 'Ⴗ' => 'ⴗ', + 'Ⴘ' => 'ⴘ', + 'Ⴙ' => 'ⴙ', + 'Ⴚ' => 'ⴚ', + 'Ⴛ' => 'ⴛ', + 'Ⴜ' => 'ⴜ', + 'Ⴝ' => 'ⴝ', + 'Ⴞ' => 'ⴞ', + 'Ⴟ' => 'ⴟ', + 'Ⴠ' => 'ⴠ', + 'Ⴡ' => 'ⴡ', + 'Ⴢ' => 'ⴢ', + 'Ⴣ' => 'ⴣ', + 'Ⴤ' => 'ⴤ', + 'Ⴥ' => 'ⴥ', + 'Ⴧ' => 'ⴧ', + 'Ⴭ' => 'ⴭ', + 'Ḁ' => 'ḁ', + 'Ḃ' => 'ḃ', + 'Ḅ' => 'ḅ', + 'Ḇ' => 'ḇ', + 'Ḉ' => 'ḉ', + 'Ḋ' => 'ḋ', + 'Ḍ' => 'ḍ', + 'Ḏ' => 'ḏ', + 'Ḑ' => 'ḑ', + 'Ḓ' => 'ḓ', + 'Ḕ' => 'ḕ', + 'Ḗ' => 'ḗ', + 'Ḙ' => 'ḙ', + 'Ḛ' => 'ḛ', + 'Ḝ' => 'ḝ', + 'Ḟ' => 'ḟ', + 'Ḡ' => 'ḡ', + 'Ḣ' => 'ḣ', + 'Ḥ' => 'ḥ', + 'Ḧ' => 'ḧ', + 'Ḩ' => 'ḩ', + 'Ḫ' => 'ḫ', + 'Ḭ' => 'ḭ', + 'Ḯ' => 'ḯ', + 'Ḱ' => 'ḱ', + 'Ḳ' => 'ḳ', + 'Ḵ' => 'ḵ', + 'Ḷ' => 'ḷ', + 'Ḹ' => 'ḹ', + 'Ḻ' => 'ḻ', + 'Ḽ' => 'ḽ', + 'Ḿ' => 'ḿ', + 'Ṁ' => 'ṁ', + 'Ṃ' => 'ṃ', + 'Ṅ' => 'ṅ', + 'Ṇ' => 'ṇ', + 'Ṉ' => 'ṉ', + 'Ṋ' => 'ṋ', + 'Ṍ' => 'ṍ', + 'Ṏ' => 'ṏ', + 'Ṑ' => 'ṑ', + 'Ṓ' => 'ṓ', + 'Ṕ' => 'ṕ', + 'Ṗ' => 'ṗ', + 'Ṙ' => 'ṙ', + 'Ṛ' => 'ṛ', + 'Ṝ' => 'ṝ', + 'Ṟ' => 'ṟ', + 'Ṡ' => 'ṡ', + 'Ṣ' => 'ṣ', + 'Ṥ' => 'ṥ', + 'Ṧ' => 'ṧ', + 'Ṩ' => 'ṩ', + 'Ṫ' => 'ṫ', + 'Ṭ' => 'ṭ', + 'Ṯ' => 'ṯ', + 'Ṱ' => 'ṱ', + 'Ṳ' => 'ṳ', + 'Ṵ' => 'ṵ', + 'Ṷ' => 'ṷ', + 'Ṹ' => 'ṹ', + 'Ṻ' => 'ṻ', + 'Ṽ' => 'ṽ', + 'Ṿ' => 'ṿ', + 'Ẁ' => 'ẁ', + 'Ẃ' => 'ẃ', + 'Ẅ' => 'ẅ', + 'Ẇ' => 'ẇ', + 'Ẉ' => 'ẉ', + 'Ẋ' => 'ẋ', + 'Ẍ' => 'ẍ', + 'Ẏ' => 'ẏ', + 'Ẑ' => 'ẑ', + 'Ẓ' => 'ẓ', + 'Ẕ' => 'ẕ', + 'ẞ' => 'ß', + 'Ạ' => 'ạ', + 'Ả' => 'ả', + 'Ấ' => 'ấ', + 'Ầ' => 'ầ', + 'Ẩ' => 'ẩ', + 'Ẫ' => 'ẫ', + 'Ậ' => 'ậ', + 'Ắ' => 'ắ', + 'Ằ' => 'ằ', + 'Ẳ' => 'ẳ', + 'Ẵ' => 'ẵ', + 'Ặ' => 'ặ', + 'Ẹ' => 'ẹ', + 'Ẻ' => 'ẻ', + 'Ẽ' => 'ẽ', + 'Ế' => 'ế', + 'Ề' => 'ề', + 'Ể' => 'ể', + 'Ễ' => 'ễ', + 'Ệ' => 'ệ', + 'Ỉ' => 'ỉ', + 'Ị' => 'ị', + 'Ọ' => 'ọ', + 'Ỏ' => 'ỏ', + 'Ố' => 'ố', + 'Ồ' => 'ồ', + 'Ổ' => 'ổ', + 'Ỗ' => 'ỗ', + 'Ộ' => 'ộ', + 'Ớ' => 'ớ', + 'Ờ' => 'ờ', + 'Ở' => 'ở', + 'Ỡ' => 'ỡ', + 'Ợ' => 'ợ', + 'Ụ' => 'ụ', + 'Ủ' => 'ủ', + 'Ứ' => 'ứ', + 'Ừ' => 'ừ', + 'Ử' => 'ử', + 'Ữ' => 'ữ', + 'Ự' => 'ự', + 'Ỳ' => 'ỳ', + 'Ỵ' => 'ỵ', + 'Ỷ' => 'ỷ', + 'Ỹ' => 'ỹ', + 'Ỻ' => 'ỻ', + 'Ỽ' => 'ỽ', + 'Ỿ' => 'ỿ', + 'Ἀ' => 'ἀ', + 'Ἁ' => 'ἁ', + 'Ἂ' => 'ἂ', + 'Ἃ' => 'ἃ', + 'Ἄ' => 'ἄ', + 'Ἅ' => 'ἅ', + 'Ἆ' => 'ἆ', + 'Ἇ' => 'ἇ', + 'Ἐ' => 'ἐ', + 'Ἑ' => 'ἑ', + 'Ἒ' => 'ἒ', + 'Ἓ' => 'ἓ', + 'Ἔ' => 'ἔ', + 'Ἕ' => 'ἕ', + 'Ἠ' => 'ἠ', + 'Ἡ' => 'ἡ', + 'Ἢ' => 'ἢ', + 'Ἣ' => 'ἣ', + 'Ἤ' => 'ἤ', + 'Ἥ' => 'ἥ', + 'Ἦ' => 'ἦ', + 'Ἧ' => 'ἧ', + 'Ἰ' => 'ἰ', + 'Ἱ' => 'ἱ', + 'Ἲ' => 'ἲ', + 'Ἳ' => 'ἳ', + 'Ἴ' => 'ἴ', + 'Ἵ' => 'ἵ', + 'Ἶ' => 'ἶ', + 'Ἷ' => 'ἷ', + 'Ὀ' => 'ὀ', + 'Ὁ' => 'ὁ', + 'Ὂ' => 'ὂ', + 'Ὃ' => 'ὃ', + 'Ὄ' => 'ὄ', + 'Ὅ' => 'ὅ', + 'Ὑ' => 'ὑ', + 'Ὓ' => 'ὓ', + 'Ὕ' => 'ὕ', + 'Ὗ' => 'ὗ', + 'Ὠ' => 'ὠ', + 'Ὡ' => 'ὡ', + 'Ὢ' => 'ὢ', + 'Ὣ' => 'ὣ', + 'Ὤ' => 'ὤ', + 'Ὥ' => 'ὥ', + 'Ὦ' => 'ὦ', + 'Ὧ' => 'ὧ', + 'ᾈ' => 'ᾀ', + 'ᾉ' => 'ᾁ', + 'ᾊ' => 'ᾂ', + 'ᾋ' => 'ᾃ', + 'ᾌ' => 'ᾄ', + 'ᾍ' => 'ᾅ', + 'ᾎ' => 'ᾆ', + 'ᾏ' => 'ᾇ', + 'ᾘ' => 'ᾐ', + 'ᾙ' => 'ᾑ', + 'ᾚ' => 'ᾒ', + 'ᾛ' => 'ᾓ', + 'ᾜ' => 'ᾔ', + 'ᾝ' => 'ᾕ', + 'ᾞ' => 'ᾖ', + 'ᾟ' => 'ᾗ', + 'ᾨ' => 'ᾠ', + 'ᾩ' => 'ᾡ', + 'ᾪ' => 'ᾢ', + 'ᾫ' => 'ᾣ', + 'ᾬ' => 'ᾤ', + 'ᾭ' => 'ᾥ', + 'ᾮ' => 'ᾦ', + 'ᾯ' => 'ᾧ', + 'Ᾰ' => 'ᾰ', + 'Ᾱ' => 'ᾱ', + 'Ὰ' => 'ὰ', + 'Ά' => 'ά', + 'ᾼ' => 'ᾳ', + 'Ὲ' => 'ὲ', + 'Έ' => 'έ', + 'Ὴ' => 'ὴ', + 'Ή' => 'ή', + 'ῌ' => 'ῃ', + 'Ῐ' => 'ῐ', + 'Ῑ' => 'ῑ', + 'Ὶ' => 'ὶ', + 'Ί' => 'ί', + 'Ῠ' => 'ῠ', + 'Ῡ' => 'ῡ', + 'Ὺ' => 'ὺ', + 'Ύ' => 'ύ', + 'Ῥ' => 'ῥ', + 'Ὸ' => 'ὸ', + 'Ό' => 'ό', + 'Ὼ' => 'ὼ', + 'Ώ' => 'ώ', + 'ῼ' => 'ῳ', + 'Ω' => 'ω', + 'K' => 'k', + 'Å' => 'å', + 'Ⅎ' => 'ⅎ', + 'Ⅰ' => 'ⅰ', + 'Ⅱ' => 'ⅱ', + 'Ⅲ' => 'ⅲ', + 'Ⅳ' => 'ⅳ', + 'Ⅴ' => 'ⅴ', + 'Ⅵ' => 'ⅵ', + 'Ⅶ' => 'ⅶ', + 'Ⅷ' => 'ⅷ', + 'Ⅸ' => 'ⅸ', + 'Ⅹ' => 'ⅹ', + 'Ⅺ' => 'ⅺ', + 'Ⅻ' => 'ⅻ', + 'Ⅼ' => 'ⅼ', + 'Ⅽ' => 'ⅽ', + 'Ⅾ' => 'ⅾ', + 'Ⅿ' => 'ⅿ', + 'Ↄ' => 'ↄ', + 'Ⓐ' => 'ⓐ', + 'Ⓑ' => 'ⓑ', + 'Ⓒ' => 'ⓒ', + 'Ⓓ' => 'ⓓ', + 'Ⓔ' => 'ⓔ', + 'Ⓕ' => 'ⓕ', + 'Ⓖ' => 'ⓖ', + 'Ⓗ' => 'ⓗ', + 'Ⓘ' => 'ⓘ', + 'Ⓙ' => 'ⓙ', + 'Ⓚ' => 'ⓚ', + 'Ⓛ' => 'ⓛ', + 'Ⓜ' => 'ⓜ', + 'Ⓝ' => 'ⓝ', + 'Ⓞ' => 'ⓞ', + 'Ⓟ' => 'ⓟ', + 'Ⓠ' => 'ⓠ', + 'Ⓡ' => 'ⓡ', + 'Ⓢ' => 'ⓢ', + 'Ⓣ' => 'ⓣ', + 'Ⓤ' => 'ⓤ', + 'Ⓥ' => 'ⓥ', + 'Ⓦ' => 'ⓦ', + 'Ⓧ' => 'ⓧ', + 'Ⓨ' => 'ⓨ', + 'Ⓩ' => 'ⓩ', + 'Ⰰ' => 'ⰰ', + 'Ⰱ' => 'ⰱ', + 'Ⰲ' => 'ⰲ', + 'Ⰳ' => 'ⰳ', + 'Ⰴ' => 'ⰴ', + 'Ⰵ' => 'ⰵ', + 'Ⰶ' => 'ⰶ', + 'Ⰷ' => 'ⰷ', + 'Ⰸ' => 'ⰸ', + 'Ⰹ' => 'ⰹ', + 'Ⰺ' => 'ⰺ', + 'Ⰻ' => 'ⰻ', + 'Ⰼ' => 'ⰼ', + 'Ⰽ' => 'ⰽ', + 'Ⰾ' => 'ⰾ', + 'Ⰿ' => 'ⰿ', + 'Ⱀ' => 'ⱀ', + 'Ⱁ' => 'ⱁ', + 'Ⱂ' => 'ⱂ', + 'Ⱃ' => 'ⱃ', + 'Ⱄ' => 'ⱄ', + 'Ⱅ' => 'ⱅ', + 'Ⱆ' => 'ⱆ', + 'Ⱇ' => 'ⱇ', + 'Ⱈ' => 'ⱈ', + 'Ⱉ' => 'ⱉ', + 'Ⱊ' => 'ⱊ', + 'Ⱋ' => 'ⱋ', + 'Ⱌ' => 'ⱌ', + 'Ⱍ' => 'ⱍ', + 'Ⱎ' => 'ⱎ', + 'Ⱏ' => 'ⱏ', + 'Ⱐ' => 'ⱐ', + 'Ⱑ' => 'ⱑ', + 'Ⱒ' => 'ⱒ', + 'Ⱓ' => 'ⱓ', + 'Ⱔ' => 'ⱔ', + 'Ⱕ' => 'ⱕ', + 'Ⱖ' => 'ⱖ', + 'Ⱗ' => 'ⱗ', + 'Ⱘ' => 'ⱘ', + 'Ⱙ' => 'ⱙ', + 'Ⱚ' => 'ⱚ', + 'Ⱛ' => 'ⱛ', + 'Ⱜ' => 'ⱜ', + 'Ⱝ' => 'ⱝ', + 'Ⱞ' => 'ⱞ', + 'Ⱡ' => 'ⱡ', + 'Ɫ' => 'ɫ', + 'Ᵽ' => 'ᵽ', + 'Ɽ' => 'ɽ', + 'Ⱨ' => 'ⱨ', + 'Ⱪ' => 'ⱪ', + 'Ⱬ' => 'ⱬ', + 'Ɑ' => 'ɑ', + 'Ɱ' => 'ɱ', + 'Ɐ' => 'ɐ', + 'Ɒ' => 'ɒ', + 'Ⱳ' => 'ⱳ', + 'Ⱶ' => 'ⱶ', + 'Ȿ' => 'ȿ', + 'Ɀ' => 'ɀ', + 'Ⲁ' => 'ⲁ', + 'Ⲃ' => 'ⲃ', + 'Ⲅ' => 'ⲅ', + 'Ⲇ' => 'ⲇ', + 'Ⲉ' => 'ⲉ', + 'Ⲋ' => 'ⲋ', + 'Ⲍ' => 'ⲍ', + 'Ⲏ' => 'ⲏ', + 'Ⲑ' => 'ⲑ', + 'Ⲓ' => 'ⲓ', + 'Ⲕ' => 'ⲕ', + 'Ⲗ' => 'ⲗ', + 'Ⲙ' => 'ⲙ', + 'Ⲛ' => 'ⲛ', + 'Ⲝ' => 'ⲝ', + 'Ⲟ' => 'ⲟ', + 'Ⲡ' => 'ⲡ', + 'Ⲣ' => 'ⲣ', + 'Ⲥ' => 'ⲥ', + 'Ⲧ' => 'ⲧ', + 'Ⲩ' => 'ⲩ', + 'Ⲫ' => 'ⲫ', + 'Ⲭ' => 'ⲭ', + 'Ⲯ' => 'ⲯ', + 'Ⲱ' => 'ⲱ', + 'Ⲳ' => 'ⲳ', + 'Ⲵ' => 'ⲵ', + 'Ⲷ' => 'ⲷ', + 'Ⲹ' => 'ⲹ', + 'Ⲻ' => 'ⲻ', + 'Ⲽ' => 'ⲽ', + 'Ⲿ' => 'ⲿ', + 'Ⳁ' => 'ⳁ', + 'Ⳃ' => 'ⳃ', + 'Ⳅ' => 'ⳅ', + 'Ⳇ' => 'ⳇ', + 'Ⳉ' => 'ⳉ', + 'Ⳋ' => 'ⳋ', + 'Ⳍ' => 'ⳍ', + 'Ⳏ' => 'ⳏ', + 'Ⳑ' => 'ⳑ', + 'Ⳓ' => 'ⳓ', + 'Ⳕ' => 'ⳕ', + 'Ⳗ' => 'ⳗ', + 'Ⳙ' => 'ⳙ', + 'Ⳛ' => 'ⳛ', + 'Ⳝ' => 'ⳝ', + 'Ⳟ' => 'ⳟ', + 'Ⳡ' => 'ⳡ', + 'Ⳣ' => 'ⳣ', + 'Ⳬ' => 'ⳬ', + 'Ⳮ' => 'ⳮ', + 'Ⳳ' => 'ⳳ', + 'Ꙁ' => 'ꙁ', + 'Ꙃ' => 'ꙃ', + 'Ꙅ' => 'ꙅ', + 'Ꙇ' => 'ꙇ', + 'Ꙉ' => 'ꙉ', + 'Ꙋ' => 'ꙋ', + 'Ꙍ' => 'ꙍ', + 'Ꙏ' => 'ꙏ', + 'Ꙑ' => 'ꙑ', + 'Ꙓ' => 'ꙓ', + 'Ꙕ' => 'ꙕ', + 'Ꙗ' => 'ꙗ', + 'Ꙙ' => 'ꙙ', + 'Ꙛ' => 'ꙛ', + 'Ꙝ' => 'ꙝ', + 'Ꙟ' => 'ꙟ', + 'Ꙡ' => 'ꙡ', + 'Ꙣ' => 'ꙣ', + 'Ꙥ' => 'ꙥ', + 'Ꙧ' => 'ꙧ', + 'Ꙩ' => 'ꙩ', + 'Ꙫ' => 'ꙫ', + 'Ꙭ' => 'ꙭ', + 'Ꚁ' => 'ꚁ', + 'Ꚃ' => 'ꚃ', + 'Ꚅ' => 'ꚅ', + 'Ꚇ' => 'ꚇ', + 'Ꚉ' => 'ꚉ', + 'Ꚋ' => 'ꚋ', + 'Ꚍ' => 'ꚍ', + 'Ꚏ' => 'ꚏ', + 'Ꚑ' => 'ꚑ', + 'Ꚓ' => 'ꚓ', + 'Ꚕ' => 'ꚕ', + 'Ꚗ' => 'ꚗ', + 'Ꚙ' => 'ꚙ', + 'Ꚛ' => 'ꚛ', + 'Ꜣ' => 'ꜣ', + 'Ꜥ' => 'ꜥ', + 'Ꜧ' => 'ꜧ', + 'Ꜩ' => 'ꜩ', + 'Ꜫ' => 'ꜫ', + 'Ꜭ' => 'ꜭ', + 'Ꜯ' => 'ꜯ', + 'Ꜳ' => 'ꜳ', + 'Ꜵ' => 'ꜵ', + 'Ꜷ' => 'ꜷ', + 'Ꜹ' => 'ꜹ', + 'Ꜻ' => 'ꜻ', + 'Ꜽ' => 'ꜽ', + 'Ꜿ' => 'ꜿ', + 'Ꝁ' => 'ꝁ', + 'Ꝃ' => 'ꝃ', + 'Ꝅ' => 'ꝅ', + 'Ꝇ' => 'ꝇ', + 'Ꝉ' => 'ꝉ', + 'Ꝋ' => 'ꝋ', + 'Ꝍ' => 'ꝍ', + 'Ꝏ' => 'ꝏ', + 'Ꝑ' => 'ꝑ', + 'Ꝓ' => 'ꝓ', + 'Ꝕ' => 'ꝕ', + 'Ꝗ' => 'ꝗ', + 'Ꝙ' => 'ꝙ', + 'Ꝛ' => 'ꝛ', + 'Ꝝ' => 'ꝝ', + 'Ꝟ' => 'ꝟ', + 'Ꝡ' => 'ꝡ', + 'Ꝣ' => 'ꝣ', + 'Ꝥ' => 'ꝥ', + 'Ꝧ' => 'ꝧ', + 'Ꝩ' => 'ꝩ', + 'Ꝫ' => 'ꝫ', + 'Ꝭ' => 'ꝭ', + 'Ꝯ' => 'ꝯ', + 'Ꝺ' => 'ꝺ', + 'Ꝼ' => 'ꝼ', + 'Ᵹ' => 'ᵹ', + 'Ꝿ' => 'ꝿ', + 'Ꞁ' => 'ꞁ', + 'Ꞃ' => 'ꞃ', + 'Ꞅ' => 'ꞅ', + 'Ꞇ' => 'ꞇ', + 'Ꞌ' => 'ꞌ', + 'Ɥ' => 'ɥ', + 'Ꞑ' => 'ꞑ', + 'Ꞓ' => 'ꞓ', + 'Ꞗ' => 'ꞗ', + 'Ꞙ' => 'ꞙ', + 'Ꞛ' => 'ꞛ', + 'Ꞝ' => 'ꞝ', + 'Ꞟ' => 'ꞟ', + 'Ꞡ' => 'ꞡ', + 'Ꞣ' => 'ꞣ', + 'Ꞥ' => 'ꞥ', + 'Ꞧ' => 'ꞧ', + 'Ꞩ' => 'ꞩ', + 'Ɦ' => 'ɦ', + 'Ɜ' => 'ɜ', + 'Ɡ' => 'ɡ', + 'Ɬ' => 'ɬ', + 'Ʞ' => 'ʞ', + 'Ʇ' => 'ʇ', + 'A' => 'a', + 'B' => 'b', + 'C' => 'c', + 'D' => 'd', + 'E' => 'e', + 'F' => 'f', + 'G' => 'g', + 'H' => 'h', + 'I' => 'i', + 'J' => 'j', + 'K' => 'k', + 'L' => 'l', + 'M' => 'm', + 'N' => 'n', + 'O' => 'o', + 'P' => 'p', + 'Q' => 'q', + 'R' => 'r', + 'S' => 's', + 'T' => 't', + 'U' => 'u', + 'V' => 'v', + 'W' => 'w', + 'X' => 'x', + 'Y' => 'y', + 'Z' => 'z', + '𐐀' => '𐐨', + '𐐁' => '𐐩', + '𐐂' => '𐐪', + '𐐃' => '𐐫', + '𐐄' => '𐐬', + '𐐅' => '𐐭', + '𐐆' => '𐐮', + '𐐇' => '𐐯', + '𐐈' => '𐐰', + '𐐉' => '𐐱', + '𐐊' => '𐐲', + '𐐋' => '𐐳', + '𐐌' => '𐐴', + '𐐍' => '𐐵', + '𐐎' => '𐐶', + '𐐏' => '𐐷', + '𐐐' => '𐐸', + '𐐑' => '𐐹', + '𐐒' => '𐐺', + '𐐓' => '𐐻', + '𐐔' => '𐐼', + '𐐕' => '𐐽', + '𐐖' => '𐐾', + '𐐗' => '𐐿', + '𐐘' => '𐑀', + '𐐙' => '𐑁', + '𐐚' => '𐑂', + '𐐛' => '𐑃', + '𐐜' => '𐑄', + '𐐝' => '𐑅', + '𐐞' => '𐑆', + '𐐟' => '𐑇', + '𐐠' => '𐑈', + '𐐡' => '𐑉', + '𐐢' => '𐑊', + '𐐣' => '𐑋', + '𐐤' => '𐑌', + '𐐥' => '𐑍', + '𐐦' => '𐑎', + '𐐧' => '𐑏', + '𑢠' => '𑣀', + '𑢡' => '𑣁', + '𑢢' => '𑣂', + '𑢣' => '𑣃', + '𑢤' => '𑣄', + '𑢥' => '𑣅', + '𑢦' => '𑣆', + '𑢧' => '𑣇', + '𑢨' => '𑣈', + '𑢩' => '𑣉', + '𑢪' => '𑣊', + '𑢫' => '𑣋', + '𑢬' => '𑣌', + '𑢭' => '𑣍', + '𑢮' => '𑣎', + '𑢯' => '𑣏', + '𑢰' => '𑣐', + '𑢱' => '𑣑', + '𑢲' => '𑣒', + '𑢳' => '𑣓', + '𑢴' => '𑣔', + '𑢵' => '𑣕', + '𑢶' => '𑣖', + '𑢷' => '𑣗', + '𑢸' => '𑣘', + '𑢹' => '𑣙', + '𑢺' => '𑣚', + '𑢻' => '𑣛', + '𑢼' => '𑣜', + '𑢽' => '𑣝', + '𑢾' => '𑣞', + '𑢿' => '𑣟', +); + +$result =& $data; +unset($data); + +return $result; diff --git a/lib/silex/vendor/symfony/polyfill-mbstring/Resources/unidata/upperCase.php b/lib/silex/vendor/symfony/polyfill-mbstring/Resources/unidata/upperCase.php new file mode 100644 index 000000000..ec9422121 --- /dev/null +++ b/lib/silex/vendor/symfony/polyfill-mbstring/Resources/unidata/upperCase.php @@ -0,0 +1,1109 @@ + 'A', + 'b' => 'B', + 'c' => 'C', + 'd' => 'D', + 'e' => 'E', + 'f' => 'F', + 'g' => 'G', + 'h' => 'H', + 'i' => 'I', + 'j' => 'J', + 'k' => 'K', + 'l' => 'L', + 'm' => 'M', + 'n' => 'N', + 'o' => 'O', + 'p' => 'P', + 'q' => 'Q', + 'r' => 'R', + 's' => 'S', + 't' => 'T', + 'u' => 'U', + 'v' => 'V', + 'w' => 'W', + 'x' => 'X', + 'y' => 'Y', + 'z' => 'Z', + 'µ' => 'Μ', + 'à' => 'À', + 'á' => 'Á', + 'â' => 'Â', + 'ã' => 'Ã', + 'ä' => 'Ä', + 'å' => 'Å', + 'æ' => 'Æ', + 'ç' => 'Ç', + 'è' => 'È', + 'é' => 'É', + 'ê' => 'Ê', + 'ë' => 'Ë', + 'ì' => 'Ì', + 'í' => 'Í', + 'î' => 'Î', + 'ï' => 'Ï', + 'ð' => 'Ð', + 'ñ' => 'Ñ', + 'ò' => 'Ò', + 'ó' => 'Ó', + 'ô' => 'Ô', + 'õ' => 'Õ', + 'ö' => 'Ö', + 'ø' => 'Ø', + 'ù' => 'Ù', + 'ú' => 'Ú', + 'û' => 'Û', + 'ü' => 'Ü', + 'ý' => 'Ý', + 'þ' => 'Þ', + 'ÿ' => 'Ÿ', + 'ā' => 'Ā', + 'ă' => 'Ă', + 'ą' => 'Ą', + 'ć' => 'Ć', + 'ĉ' => 'Ĉ', + 'ċ' => 'Ċ', + 'č' => 'Č', + 'ď' => 'Ď', + 'đ' => 'Đ', + 'ē' => 'Ē', + 'ĕ' => 'Ĕ', + 'ė' => 'Ė', + 'ę' => 'Ę', + 'ě' => 'Ě', + 'ĝ' => 'Ĝ', + 'ğ' => 'Ğ', + 'ġ' => 'Ġ', + 'ģ' => 'Ģ', + 'ĥ' => 'Ĥ', + 'ħ' => 'Ħ', + 'ĩ' => 'Ĩ', + 'ī' => 'Ī', + 'ĭ' => 'Ĭ', + 'į' => 'Į', + 'ı' => 'I', + 'ij' => 'IJ', + 'ĵ' => 'Ĵ', + 'ķ' => 'Ķ', + 'ĺ' => 'Ĺ', + 'ļ' => 'Ļ', + 'ľ' => 'Ľ', + 'ŀ' => 'Ŀ', + 'ł' => 'Ł', + 'ń' => 'Ń', + 'ņ' => 'Ņ', + 'ň' => 'Ň', + 'ŋ' => 'Ŋ', + 'ō' => 'Ō', + 'ŏ' => 'Ŏ', + 'ő' => 'Ő', + 'œ' => 'Œ', + 'ŕ' => 'Ŕ', + 'ŗ' => 'Ŗ', + 'ř' => 'Ř', + 'ś' => 'Ś', + 'ŝ' => 'Ŝ', + 'ş' => 'Ş', + 'š' => 'Š', + 'ţ' => 'Ţ', + 'ť' => 'Ť', + 'ŧ' => 'Ŧ', + 'ũ' => 'Ũ', + 'ū' => 'Ū', + 'ŭ' => 'Ŭ', + 'ů' => 'Ů', + 'ű' => 'Ű', + 'ų' => 'Ų', + 'ŵ' => 'Ŵ', + 'ŷ' => 'Ŷ', + 'ź' => 'Ź', + 'ż' => 'Ż', + 'ž' => 'Ž', + 'ſ' => 'S', + 'ƀ' => 'Ƀ', + 'ƃ' => 'Ƃ', + 'ƅ' => 'Ƅ', + 'ƈ' => 'Ƈ', + 'ƌ' => 'Ƌ', + 'ƒ' => 'Ƒ', + 'ƕ' => 'Ƕ', + 'ƙ' => 'Ƙ', + 'ƚ' => 'Ƚ', + 'ƞ' => 'Ƞ', + 'ơ' => 'Ơ', + 'ƣ' => 'Ƣ', + 'ƥ' => 'Ƥ', + 'ƨ' => 'Ƨ', + 'ƭ' => 'Ƭ', + 'ư' => 'Ư', + 'ƴ' => 'Ƴ', + 'ƶ' => 'Ƶ', + 'ƹ' => 'Ƹ', + 'ƽ' => 'Ƽ', + 'ƿ' => 'Ƿ', + 'Dž' => 'DŽ', + 'dž' => 'DŽ', + 'Lj' => 'LJ', + 'lj' => 'LJ', + 'Nj' => 'NJ', + 'nj' => 'NJ', + 'ǎ' => 'Ǎ', + 'ǐ' => 'Ǐ', + 'ǒ' => 'Ǒ', + 'ǔ' => 'Ǔ', + 'ǖ' => 'Ǖ', + 'ǘ' => 'Ǘ', + 'ǚ' => 'Ǚ', + 'ǜ' => 'Ǜ', + 'ǝ' => 'Ǝ', + 'ǟ' => 'Ǟ', + 'ǡ' => 'Ǡ', + 'ǣ' => 'Ǣ', + 'ǥ' => 'Ǥ', + 'ǧ' => 'Ǧ', + 'ǩ' => 'Ǩ', + 'ǫ' => 'Ǫ', + 'ǭ' => 'Ǭ', + 'ǯ' => 'Ǯ', + 'Dz' => 'DZ', + 'dz' => 'DZ', + 'ǵ' => 'Ǵ', + 'ǹ' => 'Ǹ', + 'ǻ' => 'Ǻ', + 'ǽ' => 'Ǽ', + 'ǿ' => 'Ǿ', + 'ȁ' => 'Ȁ', + 'ȃ' => 'Ȃ', + 'ȅ' => 'Ȅ', + 'ȇ' => 'Ȇ', + 'ȉ' => 'Ȉ', + 'ȋ' => 'Ȋ', + 'ȍ' => 'Ȍ', + 'ȏ' => 'Ȏ', + 'ȑ' => 'Ȑ', + 'ȓ' => 'Ȓ', + 'ȕ' => 'Ȕ', + 'ȗ' => 'Ȗ', + 'ș' => 'Ș', + 'ț' => 'Ț', + 'ȝ' => 'Ȝ', + 'ȟ' => 'Ȟ', + 'ȣ' => 'Ȣ', + 'ȥ' => 'Ȥ', + 'ȧ' => 'Ȧ', + 'ȩ' => 'Ȩ', + 'ȫ' => 'Ȫ', + 'ȭ' => 'Ȭ', + 'ȯ' => 'Ȯ', + 'ȱ' => 'Ȱ', + 'ȳ' => 'Ȳ', + 'ȼ' => 'Ȼ', + 'ȿ' => 'Ȿ', + 'ɀ' => 'Ɀ', + 'ɂ' => 'Ɂ', + 'ɇ' => 'Ɇ', + 'ɉ' => 'Ɉ', + 'ɋ' => 'Ɋ', + 'ɍ' => 'Ɍ', + 'ɏ' => 'Ɏ', + 'ɐ' => 'Ɐ', + 'ɑ' => 'Ɑ', + 'ɒ' => 'Ɒ', + 'ɓ' => 'Ɓ', + 'ɔ' => 'Ɔ', + 'ɖ' => 'Ɖ', + 'ɗ' => 'Ɗ', + 'ə' => 'Ə', + 'ɛ' => 'Ɛ', + 'ɜ' => 'Ɜ', + 'ɠ' => 'Ɠ', + 'ɡ' => 'Ɡ', + 'ɣ' => 'Ɣ', + 'ɥ' => 'Ɥ', + 'ɦ' => 'Ɦ', + 'ɨ' => 'Ɨ', + 'ɩ' => 'Ɩ', + 'ɫ' => 'Ɫ', + 'ɬ' => 'Ɬ', + 'ɯ' => 'Ɯ', + 'ɱ' => 'Ɱ', + 'ɲ' => 'Ɲ', + 'ɵ' => 'Ɵ', + 'ɽ' => 'Ɽ', + 'ʀ' => 'Ʀ', + 'ʃ' => 'Ʃ', + 'ʇ' => 'Ʇ', + 'ʈ' => 'Ʈ', + 'ʉ' => 'Ʉ', + 'ʊ' => 'Ʊ', + 'ʋ' => 'Ʋ', + 'ʌ' => 'Ʌ', + 'ʒ' => 'Ʒ', + 'ʞ' => 'Ʞ', + 'ͅ' => 'Ι', + 'ͱ' => 'Ͱ', + 'ͳ' => 'Ͳ', + 'ͷ' => 'Ͷ', + 'ͻ' => 'Ͻ', + 'ͼ' => 'Ͼ', + 'ͽ' => 'Ͽ', + 'ά' => 'Ά', + 'έ' => 'Έ', + 'ή' => 'Ή', + 'ί' => 'Ί', + 'α' => 'Α', + 'β' => 'Β', + 'γ' => 'Γ', + 'δ' => 'Δ', + 'ε' => 'Ε', + 'ζ' => 'Ζ', + 'η' => 'Η', + 'θ' => 'Θ', + 'ι' => 'Ι', + 'κ' => 'Κ', + 'λ' => 'Λ', + 'μ' => 'Μ', + 'ν' => 'Ν', + 'ξ' => 'Ξ', + 'ο' => 'Ο', + 'π' => 'Π', + 'ρ' => 'Ρ', + 'ς' => 'Σ', + 'σ' => 'Σ', + 'τ' => 'Τ', + 'υ' => 'Υ', + 'φ' => 'Φ', + 'χ' => 'Χ', + 'ψ' => 'Ψ', + 'ω' => 'Ω', + 'ϊ' => 'Ϊ', + 'ϋ' => 'Ϋ', + 'ό' => 'Ό', + 'ύ' => 'Ύ', + 'ώ' => 'Ώ', + 'ϐ' => 'Β', + 'ϑ' => 'Θ', + 'ϕ' => 'Φ', + 'ϖ' => 'Π', + 'ϗ' => 'Ϗ', + 'ϙ' => 'Ϙ', + 'ϛ' => 'Ϛ', + 'ϝ' => 'Ϝ', + 'ϟ' => 'Ϟ', + 'ϡ' => 'Ϡ', + 'ϣ' => 'Ϣ', + 'ϥ' => 'Ϥ', + 'ϧ' => 'Ϧ', + 'ϩ' => 'Ϩ', + 'ϫ' => 'Ϫ', + 'ϭ' => 'Ϭ', + 'ϯ' => 'Ϯ', + 'ϰ' => 'Κ', + 'ϱ' => 'Ρ', + 'ϲ' => 'Ϲ', + 'ϳ' => 'Ϳ', + 'ϵ' => 'Ε', + 'ϸ' => 'Ϸ', + 'ϻ' => 'Ϻ', + 'а' => 'А', + 'б' => 'Б', + 'в' => 'В', + 'г' => 'Г', + 'д' => 'Д', + 'е' => 'Е', + 'ж' => 'Ж', + 'з' => 'З', + 'и' => 'И', + 'й' => 'Й', + 'к' => 'К', + 'л' => 'Л', + 'м' => 'М', + 'н' => 'Н', + 'о' => 'О', + 'п' => 'П', + 'р' => 'Р', + 'с' => 'С', + 'т' => 'Т', + 'у' => 'У', + 'ф' => 'Ф', + 'х' => 'Х', + 'ц' => 'Ц', + 'ч' => 'Ч', + 'ш' => 'Ш', + 'щ' => 'Щ', + 'ъ' => 'Ъ', + 'ы' => 'Ы', + 'ь' => 'Ь', + 'э' => 'Э', + 'ю' => 'Ю', + 'я' => 'Я', + 'ѐ' => 'Ѐ', + 'ё' => 'Ё', + 'ђ' => 'Ђ', + 'ѓ' => 'Ѓ', + 'є' => 'Є', + 'ѕ' => 'Ѕ', + 'і' => 'І', + 'ї' => 'Ї', + 'ј' => 'Ј', + 'љ' => 'Љ', + 'њ' => 'Њ', + 'ћ' => 'Ћ', + 'ќ' => 'Ќ', + 'ѝ' => 'Ѝ', + 'ў' => 'Ў', + 'џ' => 'Џ', + 'ѡ' => 'Ѡ', + 'ѣ' => 'Ѣ', + 'ѥ' => 'Ѥ', + 'ѧ' => 'Ѧ', + 'ѩ' => 'Ѩ', + 'ѫ' => 'Ѫ', + 'ѭ' => 'Ѭ', + 'ѯ' => 'Ѯ', + 'ѱ' => 'Ѱ', + 'ѳ' => 'Ѳ', + 'ѵ' => 'Ѵ', + 'ѷ' => 'Ѷ', + 'ѹ' => 'Ѹ', + 'ѻ' => 'Ѻ', + 'ѽ' => 'Ѽ', + 'ѿ' => 'Ѿ', + 'ҁ' => 'Ҁ', + 'ҋ' => 'Ҋ', + 'ҍ' => 'Ҍ', + 'ҏ' => 'Ҏ', + 'ґ' => 'Ґ', + 'ғ' => 'Ғ', + 'ҕ' => 'Ҕ', + 'җ' => 'Җ', + 'ҙ' => 'Ҙ', + 'қ' => 'Қ', + 'ҝ' => 'Ҝ', + 'ҟ' => 'Ҟ', + 'ҡ' => 'Ҡ', + 'ң' => 'Ң', + 'ҥ' => 'Ҥ', + 'ҧ' => 'Ҧ', + 'ҩ' => 'Ҩ', + 'ҫ' => 'Ҫ', + 'ҭ' => 'Ҭ', + 'ү' => 'Ү', + 'ұ' => 'Ұ', + 'ҳ' => 'Ҳ', + 'ҵ' => 'Ҵ', + 'ҷ' => 'Ҷ', + 'ҹ' => 'Ҹ', + 'һ' => 'Һ', + 'ҽ' => 'Ҽ', + 'ҿ' => 'Ҿ', + 'ӂ' => 'Ӂ', + 'ӄ' => 'Ӄ', + 'ӆ' => 'Ӆ', + 'ӈ' => 'Ӈ', + 'ӊ' => 'Ӊ', + 'ӌ' => 'Ӌ', + 'ӎ' => 'Ӎ', + 'ӏ' => 'Ӏ', + 'ӑ' => 'Ӑ', + 'ӓ' => 'Ӓ', + 'ӕ' => 'Ӕ', + 'ӗ' => 'Ӗ', + 'ә' => 'Ә', + 'ӛ' => 'Ӛ', + 'ӝ' => 'Ӝ', + 'ӟ' => 'Ӟ', + 'ӡ' => 'Ӡ', + 'ӣ' => 'Ӣ', + 'ӥ' => 'Ӥ', + 'ӧ' => 'Ӧ', + 'ө' => 'Ө', + 'ӫ' => 'Ӫ', + 'ӭ' => 'Ӭ', + 'ӯ' => 'Ӯ', + 'ӱ' => 'Ӱ', + 'ӳ' => 'Ӳ', + 'ӵ' => 'Ӵ', + 'ӷ' => 'Ӷ', + 'ӹ' => 'Ӹ', + 'ӻ' => 'Ӻ', + 'ӽ' => 'Ӽ', + 'ӿ' => 'Ӿ', + 'ԁ' => 'Ԁ', + 'ԃ' => 'Ԃ', + 'ԅ' => 'Ԅ', + 'ԇ' => 'Ԇ', + 'ԉ' => 'Ԉ', + 'ԋ' => 'Ԋ', + 'ԍ' => 'Ԍ', + 'ԏ' => 'Ԏ', + 'ԑ' => 'Ԑ', + 'ԓ' => 'Ԓ', + 'ԕ' => 'Ԕ', + 'ԗ' => 'Ԗ', + 'ԙ' => 'Ԙ', + 'ԛ' => 'Ԛ', + 'ԝ' => 'Ԝ', + 'ԟ' => 'Ԟ', + 'ԡ' => 'Ԡ', + 'ԣ' => 'Ԣ', + 'ԥ' => 'Ԥ', + 'ԧ' => 'Ԧ', + 'ԩ' => 'Ԩ', + 'ԫ' => 'Ԫ', + 'ԭ' => 'Ԭ', + 'ԯ' => 'Ԯ', + 'ա' => 'Ա', + 'բ' => 'Բ', + 'գ' => 'Գ', + 'դ' => 'Դ', + 'ե' => 'Ե', + 'զ' => 'Զ', + 'է' => 'Է', + 'ը' => 'Ը', + 'թ' => 'Թ', + 'ժ' => 'Ժ', + 'ի' => 'Ի', + 'լ' => 'Լ', + 'խ' => 'Խ', + 'ծ' => 'Ծ', + 'կ' => 'Կ', + 'հ' => 'Հ', + 'ձ' => 'Ձ', + 'ղ' => 'Ղ', + 'ճ' => 'Ճ', + 'մ' => 'Մ', + 'յ' => 'Յ', + 'ն' => 'Ն', + 'շ' => 'Շ', + 'ո' => 'Ո', + 'չ' => 'Չ', + 'պ' => 'Պ', + 'ջ' => 'Ջ', + 'ռ' => 'Ռ', + 'ս' => 'Ս', + 'վ' => 'Վ', + 'տ' => 'Տ', + 'ր' => 'Ր', + 'ց' => 'Ց', + 'ւ' => 'Ւ', + 'փ' => 'Փ', + 'ք' => 'Ք', + 'օ' => 'Օ', + 'ֆ' => 'Ֆ', + 'ᵹ' => 'Ᵹ', + 'ᵽ' => 'Ᵽ', + 'ḁ' => 'Ḁ', + 'ḃ' => 'Ḃ', + 'ḅ' => 'Ḅ', + 'ḇ' => 'Ḇ', + 'ḉ' => 'Ḉ', + 'ḋ' => 'Ḋ', + 'ḍ' => 'Ḍ', + 'ḏ' => 'Ḏ', + 'ḑ' => 'Ḑ', + 'ḓ' => 'Ḓ', + 'ḕ' => 'Ḕ', + 'ḗ' => 'Ḗ', + 'ḙ' => 'Ḙ', + 'ḛ' => 'Ḛ', + 'ḝ' => 'Ḝ', + 'ḟ' => 'Ḟ', + 'ḡ' => 'Ḡ', + 'ḣ' => 'Ḣ', + 'ḥ' => 'Ḥ', + 'ḧ' => 'Ḧ', + 'ḩ' => 'Ḩ', + 'ḫ' => 'Ḫ', + 'ḭ' => 'Ḭ', + 'ḯ' => 'Ḯ', + 'ḱ' => 'Ḱ', + 'ḳ' => 'Ḳ', + 'ḵ' => 'Ḵ', + 'ḷ' => 'Ḷ', + 'ḹ' => 'Ḹ', + 'ḻ' => 'Ḻ', + 'ḽ' => 'Ḽ', + 'ḿ' => 'Ḿ', + 'ṁ' => 'Ṁ', + 'ṃ' => 'Ṃ', + 'ṅ' => 'Ṅ', + 'ṇ' => 'Ṇ', + 'ṉ' => 'Ṉ', + 'ṋ' => 'Ṋ', + 'ṍ' => 'Ṍ', + 'ṏ' => 'Ṏ', + 'ṑ' => 'Ṑ', + 'ṓ' => 'Ṓ', + 'ṕ' => 'Ṕ', + 'ṗ' => 'Ṗ', + 'ṙ' => 'Ṙ', + 'ṛ' => 'Ṛ', + 'ṝ' => 'Ṝ', + 'ṟ' => 'Ṟ', + 'ṡ' => 'Ṡ', + 'ṣ' => 'Ṣ', + 'ṥ' => 'Ṥ', + 'ṧ' => 'Ṧ', + 'ṩ' => 'Ṩ', + 'ṫ' => 'Ṫ', + 'ṭ' => 'Ṭ', + 'ṯ' => 'Ṯ', + 'ṱ' => 'Ṱ', + 'ṳ' => 'Ṳ', + 'ṵ' => 'Ṵ', + 'ṷ' => 'Ṷ', + 'ṹ' => 'Ṹ', + 'ṻ' => 'Ṻ', + 'ṽ' => 'Ṽ', + 'ṿ' => 'Ṿ', + 'ẁ' => 'Ẁ', + 'ẃ' => 'Ẃ', + 'ẅ' => 'Ẅ', + 'ẇ' => 'Ẇ', + 'ẉ' => 'Ẉ', + 'ẋ' => 'Ẋ', + 'ẍ' => 'Ẍ', + 'ẏ' => 'Ẏ', + 'ẑ' => 'Ẑ', + 'ẓ' => 'Ẓ', + 'ẕ' => 'Ẕ', + 'ẛ' => 'Ṡ', + 'ạ' => 'Ạ', + 'ả' => 'Ả', + 'ấ' => 'Ấ', + 'ầ' => 'Ầ', + 'ẩ' => 'Ẩ', + 'ẫ' => 'Ẫ', + 'ậ' => 'Ậ', + 'ắ' => 'Ắ', + 'ằ' => 'Ằ', + 'ẳ' => 'Ẳ', + 'ẵ' => 'Ẵ', + 'ặ' => 'Ặ', + 'ẹ' => 'Ẹ', + 'ẻ' => 'Ẻ', + 'ẽ' => 'Ẽ', + 'ế' => 'Ế', + 'ề' => 'Ề', + 'ể' => 'Ể', + 'ễ' => 'Ễ', + 'ệ' => 'Ệ', + 'ỉ' => 'Ỉ', + 'ị' => 'Ị', + 'ọ' => 'Ọ', + 'ỏ' => 'Ỏ', + 'ố' => 'Ố', + 'ồ' => 'Ồ', + 'ổ' => 'Ổ', + 'ỗ' => 'Ỗ', + 'ộ' => 'Ộ', + 'ớ' => 'Ớ', + 'ờ' => 'Ờ', + 'ở' => 'Ở', + 'ỡ' => 'Ỡ', + 'ợ' => 'Ợ', + 'ụ' => 'Ụ', + 'ủ' => 'Ủ', + 'ứ' => 'Ứ', + 'ừ' => 'Ừ', + 'ử' => 'Ử', + 'ữ' => 'Ữ', + 'ự' => 'Ự', + 'ỳ' => 'Ỳ', + 'ỵ' => 'Ỵ', + 'ỷ' => 'Ỷ', + 'ỹ' => 'Ỹ', + 'ỻ' => 'Ỻ', + 'ỽ' => 'Ỽ', + 'ỿ' => 'Ỿ', + 'ἀ' => 'Ἀ', + 'ἁ' => 'Ἁ', + 'ἂ' => 'Ἂ', + 'ἃ' => 'Ἃ', + 'ἄ' => 'Ἄ', + 'ἅ' => 'Ἅ', + 'ἆ' => 'Ἆ', + 'ἇ' => 'Ἇ', + 'ἐ' => 'Ἐ', + 'ἑ' => 'Ἑ', + 'ἒ' => 'Ἒ', + 'ἓ' => 'Ἓ', + 'ἔ' => 'Ἔ', + 'ἕ' => 'Ἕ', + 'ἠ' => 'Ἠ', + 'ἡ' => 'Ἡ', + 'ἢ' => 'Ἢ', + 'ἣ' => 'Ἣ', + 'ἤ' => 'Ἤ', + 'ἥ' => 'Ἥ', + 'ἦ' => 'Ἦ', + 'ἧ' => 'Ἧ', + 'ἰ' => 'Ἰ', + 'ἱ' => 'Ἱ', + 'ἲ' => 'Ἲ', + 'ἳ' => 'Ἳ', + 'ἴ' => 'Ἴ', + 'ἵ' => 'Ἵ', + 'ἶ' => 'Ἶ', + 'ἷ' => 'Ἷ', + 'ὀ' => 'Ὀ', + 'ὁ' => 'Ὁ', + 'ὂ' => 'Ὂ', + 'ὃ' => 'Ὃ', + 'ὄ' => 'Ὄ', + 'ὅ' => 'Ὅ', + 'ὑ' => 'Ὑ', + 'ὓ' => 'Ὓ', + 'ὕ' => 'Ὕ', + 'ὗ' => 'Ὗ', + 'ὠ' => 'Ὠ', + 'ὡ' => 'Ὡ', + 'ὢ' => 'Ὢ', + 'ὣ' => 'Ὣ', + 'ὤ' => 'Ὤ', + 'ὥ' => 'Ὥ', + 'ὦ' => 'Ὦ', + 'ὧ' => 'Ὧ', + 'ὰ' => 'Ὰ', + 'ά' => 'Ά', + 'ὲ' => 'Ὲ', + 'έ' => 'Έ', + 'ὴ' => 'Ὴ', + 'ή' => 'Ή', + 'ὶ' => 'Ὶ', + 'ί' => 'Ί', + 'ὸ' => 'Ὸ', + 'ό' => 'Ό', + 'ὺ' => 'Ὺ', + 'ύ' => 'Ύ', + 'ὼ' => 'Ὼ', + 'ώ' => 'Ώ', + 'ᾀ' => 'ᾈ', + 'ᾁ' => 'ᾉ', + 'ᾂ' => 'ᾊ', + 'ᾃ' => 'ᾋ', + 'ᾄ' => 'ᾌ', + 'ᾅ' => 'ᾍ', + 'ᾆ' => 'ᾎ', + 'ᾇ' => 'ᾏ', + 'ᾐ' => 'ᾘ', + 'ᾑ' => 'ᾙ', + 'ᾒ' => 'ᾚ', + 'ᾓ' => 'ᾛ', + 'ᾔ' => 'ᾜ', + 'ᾕ' => 'ᾝ', + 'ᾖ' => 'ᾞ', + 'ᾗ' => 'ᾟ', + 'ᾠ' => 'ᾨ', + 'ᾡ' => 'ᾩ', + 'ᾢ' => 'ᾪ', + 'ᾣ' => 'ᾫ', + 'ᾤ' => 'ᾬ', + 'ᾥ' => 'ᾭ', + 'ᾦ' => 'ᾮ', + 'ᾧ' => 'ᾯ', + 'ᾰ' => 'Ᾰ', + 'ᾱ' => 'Ᾱ', + 'ᾳ' => 'ᾼ', + 'ι' => 'Ι', + 'ῃ' => 'ῌ', + 'ῐ' => 'Ῐ', + 'ῑ' => 'Ῑ', + 'ῠ' => 'Ῠ', + 'ῡ' => 'Ῡ', + 'ῥ' => 'Ῥ', + 'ῳ' => 'ῼ', + 'ⅎ' => 'Ⅎ', + 'ⅰ' => 'Ⅰ', + 'ⅱ' => 'Ⅱ', + 'ⅲ' => 'Ⅲ', + 'ⅳ' => 'Ⅳ', + 'ⅴ' => 'Ⅴ', + 'ⅵ' => 'Ⅵ', + 'ⅶ' => 'Ⅶ', + 'ⅷ' => 'Ⅷ', + 'ⅸ' => 'Ⅸ', + 'ⅹ' => 'Ⅹ', + 'ⅺ' => 'Ⅺ', + 'ⅻ' => 'Ⅻ', + 'ⅼ' => 'Ⅼ', + 'ⅽ' => 'Ⅽ', + 'ⅾ' => 'Ⅾ', + 'ⅿ' => 'Ⅿ', + 'ↄ' => 'Ↄ', + 'ⓐ' => 'Ⓐ', + 'ⓑ' => 'Ⓑ', + 'ⓒ' => 'Ⓒ', + 'ⓓ' => 'Ⓓ', + 'ⓔ' => 'Ⓔ', + 'ⓕ' => 'Ⓕ', + 'ⓖ' => 'Ⓖ', + 'ⓗ' => 'Ⓗ', + 'ⓘ' => 'Ⓘ', + 'ⓙ' => 'Ⓙ', + 'ⓚ' => 'Ⓚ', + 'ⓛ' => 'Ⓛ', + 'ⓜ' => 'Ⓜ', + 'ⓝ' => 'Ⓝ', + 'ⓞ' => 'Ⓞ', + 'ⓟ' => 'Ⓟ', + 'ⓠ' => 'Ⓠ', + 'ⓡ' => 'Ⓡ', + 'ⓢ' => 'Ⓢ', + 'ⓣ' => 'Ⓣ', + 'ⓤ' => 'Ⓤ', + 'ⓥ' => 'Ⓥ', + 'ⓦ' => 'Ⓦ', + 'ⓧ' => 'Ⓧ', + 'ⓨ' => 'Ⓨ', + 'ⓩ' => 'Ⓩ', + 'ⰰ' => 'Ⰰ', + 'ⰱ' => 'Ⰱ', + 'ⰲ' => 'Ⰲ', + 'ⰳ' => 'Ⰳ', + 'ⰴ' => 'Ⰴ', + 'ⰵ' => 'Ⰵ', + 'ⰶ' => 'Ⰶ', + 'ⰷ' => 'Ⰷ', + 'ⰸ' => 'Ⰸ', + 'ⰹ' => 'Ⰹ', + 'ⰺ' => 'Ⰺ', + 'ⰻ' => 'Ⰻ', + 'ⰼ' => 'Ⰼ', + 'ⰽ' => 'Ⰽ', + 'ⰾ' => 'Ⰾ', + 'ⰿ' => 'Ⰿ', + 'ⱀ' => 'Ⱀ', + 'ⱁ' => 'Ⱁ', + 'ⱂ' => 'Ⱂ', + 'ⱃ' => 'Ⱃ', + 'ⱄ' => 'Ⱄ', + 'ⱅ' => 'Ⱅ', + 'ⱆ' => 'Ⱆ', + 'ⱇ' => 'Ⱇ', + 'ⱈ' => 'Ⱈ', + 'ⱉ' => 'Ⱉ', + 'ⱊ' => 'Ⱊ', + 'ⱋ' => 'Ⱋ', + 'ⱌ' => 'Ⱌ', + 'ⱍ' => 'Ⱍ', + 'ⱎ' => 'Ⱎ', + 'ⱏ' => 'Ⱏ', + 'ⱐ' => 'Ⱐ', + 'ⱑ' => 'Ⱑ', + 'ⱒ' => 'Ⱒ', + 'ⱓ' => 'Ⱓ', + 'ⱔ' => 'Ⱔ', + 'ⱕ' => 'Ⱕ', + 'ⱖ' => 'Ⱖ', + 'ⱗ' => 'Ⱗ', + 'ⱘ' => 'Ⱘ', + 'ⱙ' => 'Ⱙ', + 'ⱚ' => 'Ⱚ', + 'ⱛ' => 'Ⱛ', + 'ⱜ' => 'Ⱜ', + 'ⱝ' => 'Ⱝ', + 'ⱞ' => 'Ⱞ', + 'ⱡ' => 'Ⱡ', + 'ⱥ' => 'Ⱥ', + 'ⱦ' => 'Ⱦ', + 'ⱨ' => 'Ⱨ', + 'ⱪ' => 'Ⱪ', + 'ⱬ' => 'Ⱬ', + 'ⱳ' => 'Ⱳ', + 'ⱶ' => 'Ⱶ', + 'ⲁ' => 'Ⲁ', + 'ⲃ' => 'Ⲃ', + 'ⲅ' => 'Ⲅ', + 'ⲇ' => 'Ⲇ', + 'ⲉ' => 'Ⲉ', + 'ⲋ' => 'Ⲋ', + 'ⲍ' => 'Ⲍ', + 'ⲏ' => 'Ⲏ', + 'ⲑ' => 'Ⲑ', + 'ⲓ' => 'Ⲓ', + 'ⲕ' => 'Ⲕ', + 'ⲗ' => 'Ⲗ', + 'ⲙ' => 'Ⲙ', + 'ⲛ' => 'Ⲛ', + 'ⲝ' => 'Ⲝ', + 'ⲟ' => 'Ⲟ', + 'ⲡ' => 'Ⲡ', + 'ⲣ' => 'Ⲣ', + 'ⲥ' => 'Ⲥ', + 'ⲧ' => 'Ⲧ', + 'ⲩ' => 'Ⲩ', + 'ⲫ' => 'Ⲫ', + 'ⲭ' => 'Ⲭ', + 'ⲯ' => 'Ⲯ', + 'ⲱ' => 'Ⲱ', + 'ⲳ' => 'Ⲳ', + 'ⲵ' => 'Ⲵ', + 'ⲷ' => 'Ⲷ', + 'ⲹ' => 'Ⲹ', + 'ⲻ' => 'Ⲻ', + 'ⲽ' => 'Ⲽ', + 'ⲿ' => 'Ⲿ', + 'ⳁ' => 'Ⳁ', + 'ⳃ' => 'Ⳃ', + 'ⳅ' => 'Ⳅ', + 'ⳇ' => 'Ⳇ', + 'ⳉ' => 'Ⳉ', + 'ⳋ' => 'Ⳋ', + 'ⳍ' => 'Ⳍ', + 'ⳏ' => 'Ⳏ', + 'ⳑ' => 'Ⳑ', + 'ⳓ' => 'Ⳓ', + 'ⳕ' => 'Ⳕ', + 'ⳗ' => 'Ⳗ', + 'ⳙ' => 'Ⳙ', + 'ⳛ' => 'Ⳛ', + 'ⳝ' => 'Ⳝ', + 'ⳟ' => 'Ⳟ', + 'ⳡ' => 'Ⳡ', + 'ⳣ' => 'Ⳣ', + 'ⳬ' => 'Ⳬ', + 'ⳮ' => 'Ⳮ', + 'ⳳ' => 'Ⳳ', + 'ⴀ' => 'Ⴀ', + 'ⴁ' => 'Ⴁ', + 'ⴂ' => 'Ⴂ', + 'ⴃ' => 'Ⴃ', + 'ⴄ' => 'Ⴄ', + 'ⴅ' => 'Ⴅ', + 'ⴆ' => 'Ⴆ', + 'ⴇ' => 'Ⴇ', + 'ⴈ' => 'Ⴈ', + 'ⴉ' => 'Ⴉ', + 'ⴊ' => 'Ⴊ', + 'ⴋ' => 'Ⴋ', + 'ⴌ' => 'Ⴌ', + 'ⴍ' => 'Ⴍ', + 'ⴎ' => 'Ⴎ', + 'ⴏ' => 'Ⴏ', + 'ⴐ' => 'Ⴐ', + 'ⴑ' => 'Ⴑ', + 'ⴒ' => 'Ⴒ', + 'ⴓ' => 'Ⴓ', + 'ⴔ' => 'Ⴔ', + 'ⴕ' => 'Ⴕ', + 'ⴖ' => 'Ⴖ', + 'ⴗ' => 'Ⴗ', + 'ⴘ' => 'Ⴘ', + 'ⴙ' => 'Ⴙ', + 'ⴚ' => 'Ⴚ', + 'ⴛ' => 'Ⴛ', + 'ⴜ' => 'Ⴜ', + 'ⴝ' => 'Ⴝ', + 'ⴞ' => 'Ⴞ', + 'ⴟ' => 'Ⴟ', + 'ⴠ' => 'Ⴠ', + 'ⴡ' => 'Ⴡ', + 'ⴢ' => 'Ⴢ', + 'ⴣ' => 'Ⴣ', + 'ⴤ' => 'Ⴤ', + 'ⴥ' => 'Ⴥ', + 'ⴧ' => 'Ⴧ', + 'ⴭ' => 'Ⴭ', + 'ꙁ' => 'Ꙁ', + 'ꙃ' => 'Ꙃ', + 'ꙅ' => 'Ꙅ', + 'ꙇ' => 'Ꙇ', + 'ꙉ' => 'Ꙉ', + 'ꙋ' => 'Ꙋ', + 'ꙍ' => 'Ꙍ', + 'ꙏ' => 'Ꙏ', + 'ꙑ' => 'Ꙑ', + 'ꙓ' => 'Ꙓ', + 'ꙕ' => 'Ꙕ', + 'ꙗ' => 'Ꙗ', + 'ꙙ' => 'Ꙙ', + 'ꙛ' => 'Ꙛ', + 'ꙝ' => 'Ꙝ', + 'ꙟ' => 'Ꙟ', + 'ꙡ' => 'Ꙡ', + 'ꙣ' => 'Ꙣ', + 'ꙥ' => 'Ꙥ', + 'ꙧ' => 'Ꙧ', + 'ꙩ' => 'Ꙩ', + 'ꙫ' => 'Ꙫ', + 'ꙭ' => 'Ꙭ', + 'ꚁ' => 'Ꚁ', + 'ꚃ' => 'Ꚃ', + 'ꚅ' => 'Ꚅ', + 'ꚇ' => 'Ꚇ', + 'ꚉ' => 'Ꚉ', + 'ꚋ' => 'Ꚋ', + 'ꚍ' => 'Ꚍ', + 'ꚏ' => 'Ꚏ', + 'ꚑ' => 'Ꚑ', + 'ꚓ' => 'Ꚓ', + 'ꚕ' => 'Ꚕ', + 'ꚗ' => 'Ꚗ', + 'ꚙ' => 'Ꚙ', + 'ꚛ' => 'Ꚛ', + 'ꜣ' => 'Ꜣ', + 'ꜥ' => 'Ꜥ', + 'ꜧ' => 'Ꜧ', + 'ꜩ' => 'Ꜩ', + 'ꜫ' => 'Ꜫ', + 'ꜭ' => 'Ꜭ', + 'ꜯ' => 'Ꜯ', + 'ꜳ' => 'Ꜳ', + 'ꜵ' => 'Ꜵ', + 'ꜷ' => 'Ꜷ', + 'ꜹ' => 'Ꜹ', + 'ꜻ' => 'Ꜻ', + 'ꜽ' => 'Ꜽ', + 'ꜿ' => 'Ꜿ', + 'ꝁ' => 'Ꝁ', + 'ꝃ' => 'Ꝃ', + 'ꝅ' => 'Ꝅ', + 'ꝇ' => 'Ꝇ', + 'ꝉ' => 'Ꝉ', + 'ꝋ' => 'Ꝋ', + 'ꝍ' => 'Ꝍ', + 'ꝏ' => 'Ꝏ', + 'ꝑ' => 'Ꝑ', + 'ꝓ' => 'Ꝓ', + 'ꝕ' => 'Ꝕ', + 'ꝗ' => 'Ꝗ', + 'ꝙ' => 'Ꝙ', + 'ꝛ' => 'Ꝛ', + 'ꝝ' => 'Ꝝ', + 'ꝟ' => 'Ꝟ', + 'ꝡ' => 'Ꝡ', + 'ꝣ' => 'Ꝣ', + 'ꝥ' => 'Ꝥ', + 'ꝧ' => 'Ꝧ', + 'ꝩ' => 'Ꝩ', + 'ꝫ' => 'Ꝫ', + 'ꝭ' => 'Ꝭ', + 'ꝯ' => 'Ꝯ', + 'ꝺ' => 'Ꝺ', + 'ꝼ' => 'Ꝼ', + 'ꝿ' => 'Ꝿ', + 'ꞁ' => 'Ꞁ', + 'ꞃ' => 'Ꞃ', + 'ꞅ' => 'Ꞅ', + 'ꞇ' => 'Ꞇ', + 'ꞌ' => 'Ꞌ', + 'ꞑ' => 'Ꞑ', + 'ꞓ' => 'Ꞓ', + 'ꞗ' => 'Ꞗ', + 'ꞙ' => 'Ꞙ', + 'ꞛ' => 'Ꞛ', + 'ꞝ' => 'Ꞝ', + 'ꞟ' => 'Ꞟ', + 'ꞡ' => 'Ꞡ', + 'ꞣ' => 'Ꞣ', + 'ꞥ' => 'Ꞥ', + 'ꞧ' => 'Ꞧ', + 'ꞩ' => 'Ꞩ', + 'a' => 'A', + 'b' => 'B', + 'c' => 'C', + 'd' => 'D', + 'e' => 'E', + 'f' => 'F', + 'g' => 'G', + 'h' => 'H', + 'i' => 'I', + 'j' => 'J', + 'k' => 'K', + 'l' => 'L', + 'm' => 'M', + 'n' => 'N', + 'o' => 'O', + 'p' => 'P', + 'q' => 'Q', + 'r' => 'R', + 's' => 'S', + 't' => 'T', + 'u' => 'U', + 'v' => 'V', + 'w' => 'W', + 'x' => 'X', + 'y' => 'Y', + 'z' => 'Z', + '𐐨' => '𐐀', + '𐐩' => '𐐁', + '𐐪' => '𐐂', + '𐐫' => '𐐃', + '𐐬' => '𐐄', + '𐐭' => '𐐅', + '𐐮' => '𐐆', + '𐐯' => '𐐇', + '𐐰' => '𐐈', + '𐐱' => '𐐉', + '𐐲' => '𐐊', + '𐐳' => '𐐋', + '𐐴' => '𐐌', + '𐐵' => '𐐍', + '𐐶' => '𐐎', + '𐐷' => '𐐏', + '𐐸' => '𐐐', + '𐐹' => '𐐑', + '𐐺' => '𐐒', + '𐐻' => '𐐓', + '𐐼' => '𐐔', + '𐐽' => '𐐕', + '𐐾' => '𐐖', + '𐐿' => '𐐗', + '𐑀' => '𐐘', + '𐑁' => '𐐙', + '𐑂' => '𐐚', + '𐑃' => '𐐛', + '𐑄' => '𐐜', + '𐑅' => '𐐝', + '𐑆' => '𐐞', + '𐑇' => '𐐟', + '𐑈' => '𐐠', + '𐑉' => '𐐡', + '𐑊' => '𐐢', + '𐑋' => '𐐣', + '𐑌' => '𐐤', + '𐑍' => '𐐥', + '𐑎' => '𐐦', + '𐑏' => '𐐧', + '𑣀' => '𑢠', + '𑣁' => '𑢡', + '𑣂' => '𑢢', + '𑣃' => '𑢣', + '𑣄' => '𑢤', + '𑣅' => '𑢥', + '𑣆' => '𑢦', + '𑣇' => '𑢧', + '𑣈' => '𑢨', + '𑣉' => '𑢩', + '𑣊' => '𑢪', + '𑣋' => '𑢫', + '𑣌' => '𑢬', + '𑣍' => '𑢭', + '𑣎' => '𑢮', + '𑣏' => '𑢯', + '𑣐' => '𑢰', + '𑣑' => '𑢱', + '𑣒' => '𑢲', + '𑣓' => '𑢳', + '𑣔' => '𑢴', + '𑣕' => '𑢵', + '𑣖' => '𑢶', + '𑣗' => '𑢷', + '𑣘' => '𑢸', + '𑣙' => '𑢹', + '𑣚' => '𑢺', + '𑣛' => '𑢻', + '𑣜' => '𑢼', + '𑣝' => '𑢽', + '𑣞' => '𑢾', + '𑣟' => '𑢿', +); + +$result =& $data; +unset($data); + +return $result; diff --git a/lib/silex/vendor/symfony/polyfill-mbstring/bootstrap.php b/lib/silex/vendor/symfony/polyfill-mbstring/bootstrap.php new file mode 100644 index 000000000..337229106 --- /dev/null +++ b/lib/silex/vendor/symfony/polyfill-mbstring/bootstrap.php @@ -0,0 +1,56 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +use Symfony\Polyfill\Mbstring as p; + +if (!function_exists('mb_strlen')) { + define('MB_CASE_UPPER', 0); + define('MB_CASE_LOWER', 1); + define('MB_CASE_TITLE', 2); + + function mb_convert_encoding($s, $to, $from = null) { return p\Mbstring::mb_convert_encoding($s, $to, $from); } + function mb_decode_mimeheader($s) { return p\Mbstring::mb_decode_mimeheader($s); } + function mb_encode_mimeheader($s, $charset = null, $transferEnc = null, $lf = null, $indent = null) { return p\Mbstring::mb_encode_mimeheader($s, $charset, $transferEnc, $lf, $indent); } + function mb_convert_case($s, $mode, $enc = null) { return p\Mbstring::mb_convert_case($s, $mode, $enc); } + function mb_internal_encoding($enc = null) { return p\Mbstring::mb_internal_encoding($enc); } + function mb_language($lang = null) { return p\Mbstring::mb_language($lang); } + function mb_list_encodings() { return p\Mbstring::mb_list_encodings(); } + function mb_encoding_aliases($encoding) { return p\Mbstring::mb_encoding_aliases($encoding); } + function mb_check_encoding($var = null, $encoding = null) { return p\Mbstring::mb_check_encoding($var, $encoding); } + function mb_detect_encoding($str, $encodingList = null, $strict = false) { return p\Mbstring::mb_detect_encoding($str, $encodingList, $strict); } + function mb_detect_order($encodingList = null) { return p\Mbstring::mb_detect_order($encodingList); } + function mb_parse_str($s, &$result = array()) { parse_str($s, $result); } + function mb_strlen($s, $enc = null) { return p\Mbstring::mb_strlen($s, $enc); } + function mb_strpos($s, $needle, $offset = 0, $enc = null) { return p\Mbstring::mb_strpos($s, $needle, $offset, $enc); } + function mb_strtolower($s, $enc = null) { return p\Mbstring::mb_strtolower($s, $enc); } + function mb_strtoupper($s, $enc = null) { return p\Mbstring::mb_strtoupper($s, $enc); } + function mb_substitute_character($char = null) { return p\Mbstring::mb_substitute_character($char); } + function mb_substr($s, $start, $length = 2147483647, $enc = null) { return p\Mbstring::mb_substr($s, $start, $length, $enc); } + function mb_stripos($s, $needle, $offset = 0, $enc = null) { return p\Mbstring::mb_stripos($s, $needle, $offset, $enc); } + function mb_stristr($s, $needle, $part = false, $enc = null) { return p\Mbstring::mb_stristr($s, $needle, $part, $enc); } + function mb_strrchr($s, $needle, $part = false, $enc = null) { return p\Mbstring::mb_strrchr($s, $needle, $part, $enc); } + function mb_strrichr($s, $needle, $part = false, $enc = null) { return p\Mbstring::mb_strrichr($s, $needle, $part, $enc); } + function mb_strripos($s, $needle, $offset = 0, $enc = null) { return p\Mbstring::mb_strripos($s, $needle, $offset, $enc); } + function mb_strrpos($s, $needle, $offset = 0, $enc = null) { return p\Mbstring::mb_strrpos($s, $needle, $offset, $enc); } + function mb_strstr($s, $needle, $part = false, $enc = null) { return p\Mbstring::mb_strstr($s, $needle, $part, $enc); } + function mb_get_info($type = 'all') { return p\Mbstring::mb_get_info($type); } + function mb_http_output($enc = null) { return p\Mbstring::mb_http_output($enc); } + function mb_strwidth($s, $enc = null) { return p\Mbstring::mb_strwidth($s, $enc); } + function mb_substr_count($haystack, $needle, $enc = null) { return p\Mbstring::mb_substr_count($haystack, $needle, $enc); } + function mb_output_handler($contents, $status) { return p\Mbstring::mb_output_handler($contents, $status); } + function mb_http_input($type = '') { return p\Mbstring::mb_http_input($type); } + function mb_convert_variables($toEncoding, $fromEncoding, &$a = null, &$b = null, &$c = null, &$d = null, &$e = null, &$f = null) { return p\Mbstring::mb_convert_variables($toEncoding, $fromEncoding, $a, $b, $c, $d, $e, $f); } +} +if (!function_exists('mb_chr')) { + 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); } +} diff --git a/lib/silex/vendor/symfony/polyfill-mbstring/composer.json b/lib/silex/vendor/symfony/polyfill-mbstring/composer.json new file mode 100644 index 000000000..5946b5d82 --- /dev/null +++ b/lib/silex/vendor/symfony/polyfill-mbstring/composer.json @@ -0,0 +1,34 @@ +{ + "name": "symfony/polyfill-mbstring", + "type": "library", + "description": "Symfony polyfill for the Mbstring extension", + "keywords": ["polyfill", "shim", "compatibility", "portable", "mbstring"], + "homepage": "https://symfony.com", + "license": "MIT", + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "require": { + "php": ">=5.3.3" + }, + "autoload": { + "psr-4": { "Symfony\\Polyfill\\Mbstring\\": "" }, + "files": [ "bootstrap.php" ] + }, + "suggest": { + "ext-mbstring": "For best performance" + }, + "minimum-stability": "dev", + "extra": { + "branch-alias": { + "dev-master": "1.6-dev" + } + } +} diff --git a/lib/silex/vendor/symfony/polyfill-php70/LICENSE b/lib/silex/vendor/symfony/polyfill-php70/LICENSE new file mode 100644 index 000000000..39fa189d2 --- /dev/null +++ b/lib/silex/vendor/symfony/polyfill-php70/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2014-2016 Fabien Potencier + +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/lib/silex/vendor/symfony/polyfill-php70/Php70.php b/lib/silex/vendor/symfony/polyfill-php70/Php70.php new file mode 100644 index 000000000..e7ff0b26d --- /dev/null +++ b/lib/silex/vendor/symfony/polyfill-php70/Php70.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\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 = ''.$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(); + } + + public 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/lib/silex/vendor/symfony/polyfill-php70/README.md b/lib/silex/vendor/symfony/polyfill-php70/README.md new file mode 100644 index 000000000..04988c6f9 --- /dev/null +++ b/lib/silex/vendor/symfony/polyfill-php70/README.md @@ -0,0 +1,28 @@ +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/lib/silex/vendor/symfony/polyfill-php70/Resources/stubs/ArithmeticError.php b/lib/silex/vendor/symfony/polyfill-php70/Resources/stubs/ArithmeticError.php new file mode 100644 index 000000000..681912446 --- /dev/null +++ b/lib/silex/vendor/symfony/polyfill-php70/Resources/stubs/ArithmeticError.php @@ -0,0 +1,5 @@ + + * + * 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/lib/silex/vendor/symfony/polyfill-php70/composer.json b/lib/silex/vendor/symfony/polyfill-php70/composer.json new file mode 100644 index 000000000..1d0b8fe20 --- /dev/null +++ b/lib/silex/vendor/symfony/polyfill-php70/composer.json @@ -0,0 +1,33 @@ +{ + "name": "symfony/polyfill-php70", + "type": "library", + "description": "Symfony polyfill backporting some PHP 7.0+ features to lower PHP versions", + "keywords": ["polyfill", "shim", "compatibility", "portable"], + "homepage": "https://symfony.com", + "license": "MIT", + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "require": { + "php": ">=5.3.3", + "paragonie/random_compat": "~1.0|~2.0" + }, + "autoload": { + "psr-4": { "Symfony\\Polyfill\\Php70\\": "" }, + "files": [ "bootstrap.php" ], + "classmap": [ "Resources/stubs" ] + }, + "minimum-stability": "dev", + "extra": { + "branch-alias": { + "dev-master": "1.6-dev" + } + } +} diff --git a/lib/silex/vendor/symfony/routing/.gitignore b/lib/silex/vendor/symfony/routing/.gitignore new file mode 100644 index 000000000..c49a5d8df --- /dev/null +++ b/lib/silex/vendor/symfony/routing/.gitignore @@ -0,0 +1,3 @@ +vendor/ +composer.lock +phpunit.xml diff --git a/lib/silex/vendor/symfony/routing/Annotation/Route.php b/lib/silex/vendor/symfony/routing/Annotation/Route.php new file mode 100644 index 000000000..5b3cbeaab --- /dev/null +++ b/lib/silex/vendor/symfony/routing/Annotation/Route.php @@ -0,0 +1,144 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Routing\Annotation; + +/** + * Annotation class for @Route(). + * + * @Annotation + * @Target({"CLASS", "METHOD"}) + * + * @author Fabien Potencier + */ +class Route +{ + private $path; + private $name; + private $requirements = array(); + private $options = array(); + private $defaults = array(); + private $host; + private $methods = array(); + private $schemes = array(); + private $condition; + + /** + * @param array $data An array of key/value parameters + * + * @throws \BadMethodCallException + */ + public function __construct(array $data) + { + if (isset($data['value'])) { + $data['path'] = $data['value']; + unset($data['value']); + } + + foreach ($data as $key => $value) { + $method = 'set'.str_replace('_', '', $key); + if (!method_exists($this, $method)) { + throw new \BadMethodCallException(sprintf('Unknown property "%s" on annotation "%s".', $key, get_class($this))); + } + $this->$method($value); + } + } + + public function setPath($path) + { + $this->path = $path; + } + + public function getPath() + { + return $this->path; + } + + public function setHost($pattern) + { + $this->host = $pattern; + } + + public function getHost() + { + return $this->host; + } + + public function setName($name) + { + $this->name = $name; + } + + public function getName() + { + return $this->name; + } + + public function setRequirements($requirements) + { + $this->requirements = $requirements; + } + + public function getRequirements() + { + return $this->requirements; + } + + public function setOptions($options) + { + $this->options = $options; + } + + public function getOptions() + { + return $this->options; + } + + public function setDefaults($defaults) + { + $this->defaults = $defaults; + } + + public function getDefaults() + { + return $this->defaults; + } + + public function setSchemes($schemes) + { + $this->schemes = is_array($schemes) ? $schemes : array($schemes); + } + + public function getSchemes() + { + return $this->schemes; + } + + public function setMethods($methods) + { + $this->methods = is_array($methods) ? $methods : array($methods); + } + + public function getMethods() + { + return $this->methods; + } + + public function setCondition($condition) + { + $this->condition = $condition; + } + + public function getCondition() + { + return $this->condition; + } +} diff --git a/lib/silex/vendor/symfony/routing/CHANGELOG.md b/lib/silex/vendor/symfony/routing/CHANGELOG.md new file mode 100644 index 000000000..e278f8b1e --- /dev/null +++ b/lib/silex/vendor/symfony/routing/CHANGELOG.md @@ -0,0 +1,228 @@ +CHANGELOG +========= + +3.4.0 +----- + + * Added `NoConfigurationException`. + * Added the possibility to define a prefix for all routes of a controller via @Route(name="prefix_") + * Added support for prioritized routing loaders. + * Add matched and default parameters to redirect responses + * Added support for a `controller` keyword for configuring route controllers in YAML and XML configurations. + +3.3.0 +----- + + * [DEPRECATION] Class parameters have been deprecated and will be removed in 4.0. + * router.options.generator_class + * router.options.generator_base_class + * router.options.generator_dumper_class + * router.options.matcher_class + * router.options.matcher_base_class + * router.options.matcher_dumper_class + * router.options.matcher.cache_class + * router.options.generator.cache_class + +3.2.0 +----- + + * Added support for `bool`, `int`, `float`, `string`, `list` and `map` defaults in XML configurations. + * Added support for UTF-8 requirements + +2.8.0 +----- + + * allowed specifying a directory to recursively load all routing configuration files it contains + * Added ObjectRouteLoader and ServiceRouteLoader that allow routes to be loaded + by calling a method on an object/service. + * [DEPRECATION] Deprecated the hardcoded value for the `$referenceType` argument of the `UrlGeneratorInterface::generate` method. + Use the constants defined in the `UrlGeneratorInterface` instead. + + Before: + + ```php + $router->generate('blog_show', array('slug' => 'my-blog-post'), true); + ``` + + After: + + ```php + use Symfony\Component\Routing\Generator\UrlGeneratorInterface; + + $router->generate('blog_show', array('slug' => 'my-blog-post'), UrlGeneratorInterface::ABSOLUTE_URL); + ``` + +2.5.0 +----- + + * [DEPRECATION] The `ApacheMatcherDumper` and `ApacheUrlMatcher` were deprecated and + will be removed in Symfony 3.0, since the performance gains were minimal and + it's hard to replicate the behaviour of PHP implementation. + +2.3.0 +----- + + * added RequestContext::getQueryString() + +2.2.0 +----- + + * [DEPRECATION] Several route settings have been renamed (the old ones will be removed in 3.0): + + * The `pattern` setting for a route has been deprecated in favor of `path` + * The `_scheme` and `_method` requirements have been moved to the `schemes` and `methods` settings + + Before: + + ```yaml + article_edit: + pattern: /article/{id} + requirements: { '_method': 'POST|PUT', '_scheme': 'https', 'id': '\d+' } + ``` + + ```xml + + POST|PUT + https + \d+ + + ``` + + ```php + $route = new Route(); + $route->setPattern('/article/{id}'); + $route->setRequirement('_method', 'POST|PUT'); + $route->setRequirement('_scheme', 'https'); + ``` + + After: + + ```yaml + article_edit: + path: /article/{id} + methods: [POST, PUT] + schemes: https + requirements: { 'id': '\d+' } + ``` + + ```xml + + \d+ + + ``` + + ```php + $route = new Route(); + $route->setPath('/article/{id}'); + $route->setMethods(array('POST', 'PUT')); + $route->setSchemes('https'); + ``` + + * [BC BREAK] RouteCollection does not behave like a tree structure anymore but as + a flat array of Routes. So when using PHP to build the RouteCollection, you must + make sure to add routes to the sub-collection before adding it to the parent + collection (this is not relevant when using YAML or XML for Route definitions). + + Before: + + ```php + $rootCollection = new RouteCollection(); + $subCollection = new RouteCollection(); + $rootCollection->addCollection($subCollection); + $subCollection->add('foo', new Route('/foo')); + ``` + + After: + + ```php + $rootCollection = new RouteCollection(); + $subCollection = new RouteCollection(); + $subCollection->add('foo', new Route('/foo')); + $rootCollection->addCollection($subCollection); + ``` + + Also one must call `addCollection` from the bottom to the top hierarchy. + So the correct sequence is the following (and not the reverse): + + ```php + $childCollection->addCollection($grandchildCollection); + $rootCollection->addCollection($childCollection); + ``` + + * [DEPRECATION] The methods `RouteCollection::getParent()` and `RouteCollection::getRoot()` + have been deprecated and will be removed in Symfony 2.3. + * [BC BREAK] Misusing the `RouteCollection::addPrefix` method to add defaults, requirements + or options without adding a prefix is not supported anymore. So if you called `addPrefix` + with an empty prefix or `/` only (both have no relevance), like + `addPrefix('', $defaultsArray, $requirementsArray, $optionsArray)` + you need to use the new dedicated methods `addDefaults($defaultsArray)`, + `addRequirements($requirementsArray)` or `addOptions($optionsArray)` instead. + * [DEPRECATION] The `$options` parameter to `RouteCollection::addPrefix()` has been deprecated + because adding options has nothing to do with adding a path prefix. If you want to add options + to all child routes of a RouteCollection, you can use `addOptions()`. + * [DEPRECATION] The method `RouteCollection::getPrefix()` has been deprecated + because it suggested that all routes in the collection would have this prefix, which is + not necessarily true. On top of that, since there is no tree structure anymore, this method + is also useless. Don't worry about performance, prefix optimization for matching is still done + in the dumper, which was also improved in 2.2.0 to find even more grouping possibilities. + * [DEPRECATION] `RouteCollection::addCollection(RouteCollection $collection)` should now only be + used with a single parameter. The other params `$prefix`, `$default`, `$requirements` and `$options` + will still work, but have been deprecated. The `addPrefix` method should be used for this + use-case instead. + Before: `$parentCollection->addCollection($collection, '/prefix', array(...), array(...))` + After: + ```php + $collection->addPrefix('/prefix', array(...), array(...)); + $parentCollection->addCollection($collection); + ``` + * added support for the method default argument values when defining a @Route + * Adjacent placeholders without separator work now, e.g. `/{x}{y}{z}.{_format}`. + * Characters that function as separator between placeholders are now whitelisted + to fix routes with normal text around a variable, e.g. `/prefix{var}suffix`. + * [BC BREAK] The default requirement of a variable has been changed slightly. + Previously it disallowed the previous and the next char around a variable. Now + it disallows the slash (`/`) and the next char. Using the previous char added + no value and was problematic because the route `/index.{_format}` would be + matched by `/index.ht/ml`. + * The default requirement now uses possessive quantifiers when possible which + improves matching performance by up to 20% because it prevents backtracking + when it's not needed. + * The ConfigurableRequirementsInterface can now also be used to disable the requirements + check on URL generation completely by calling `setStrictRequirements(null)`. It + improves performance in production environment as you should know that params always + pass the requirements (otherwise it would break your link anyway). + * There is no restriction on the route name anymore. So non-alphanumeric characters + are now also allowed. + * [BC BREAK] `RouteCompilerInterface::compile(Route $route)` was made static + (only relevant if you implemented your own RouteCompiler). + * Added possibility to generate relative paths and network paths in the UrlGenerator, e.g. + "../parent-file" and "//example.com/dir/file". The third parameter in + `UrlGeneratorInterface::generate($name, $parameters = array(), $referenceType = self::ABSOLUTE_PATH)` + now accepts more values and you should use the constants defined in `UrlGeneratorInterface` for + claritiy. The old method calls with a Boolean parameter will continue to work because they + equal the signature using the constants. + +2.1.0 +----- + + * added RequestMatcherInterface + * added RequestContext::fromRequest() + * the UrlMatcher does not throw a \LogicException anymore when the required + scheme is not the current one + * added TraceableUrlMatcher + * added the possibility to define options, default values and requirements + for placeholders in prefix, including imported routes + * added RouterInterface::getRouteCollection + * [BC BREAK] the UrlMatcher urldecodes the route parameters only once, they + were decoded twice before. Note that the `urldecode()` calls have been + changed for a single `rawurldecode()` in order to support `+` for input + paths. + * added RouteCollection::getRoot method to retrieve the root of a + RouteCollection tree + * [BC BREAK] made RouteCollection::setParent private which could not have + been used anyway without creating inconsistencies + * [BC BREAK] RouteCollection::remove also removes a route from parent + collections (not only from its children) + * added ConfigurableRequirementsInterface that allows to disable exceptions + (and generate empty URLs instead) when generating a route with an invalid + parameter value diff --git a/lib/silex/vendor/symfony/routing/CompiledRoute.php b/lib/silex/vendor/symfony/routing/CompiledRoute.php new file mode 100644 index 000000000..8ecf5153c --- /dev/null +++ b/lib/silex/vendor/symfony/routing/CompiledRoute.php @@ -0,0 +1,169 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Routing; + +/** + * CompiledRoutes are returned by the RouteCompiler class. + * + * @author Fabien Potencier + */ +class CompiledRoute implements \Serializable +{ + private $variables; + private $tokens; + private $staticPrefix; + private $regex; + private $pathVariables; + private $hostVariables; + private $hostRegex; + private $hostTokens; + + /** + * @param string $staticPrefix The static prefix of the compiled route + * @param string $regex The regular expression to use to match this route + * @param array $tokens An array of tokens to use to generate URL for this route + * @param array $pathVariables An array of path variables + * @param string|null $hostRegex Host regex + * @param array $hostTokens Host tokens + * @param array $hostVariables An array of host variables + * @param array $variables An array of variables (variables defined in the path and in the host patterns) + */ + public function __construct($staticPrefix, $regex, array $tokens, array $pathVariables, $hostRegex = null, array $hostTokens = array(), array $hostVariables = array(), array $variables = array()) + { + $this->staticPrefix = (string) $staticPrefix; + $this->regex = $regex; + $this->tokens = $tokens; + $this->pathVariables = $pathVariables; + $this->hostRegex = $hostRegex; + $this->hostTokens = $hostTokens; + $this->hostVariables = $hostVariables; + $this->variables = $variables; + } + + /** + * {@inheritdoc} + */ + public function serialize() + { + return serialize(array( + 'vars' => $this->variables, + 'path_prefix' => $this->staticPrefix, + 'path_regex' => $this->regex, + 'path_tokens' => $this->tokens, + 'path_vars' => $this->pathVariables, + 'host_regex' => $this->hostRegex, + 'host_tokens' => $this->hostTokens, + 'host_vars' => $this->hostVariables, + )); + } + + /** + * {@inheritdoc} + */ + public function unserialize($serialized) + { + if (\PHP_VERSION_ID >= 70000) { + $data = unserialize($serialized, array('allowed_classes' => false)); + } else { + $data = unserialize($serialized); + } + + $this->variables = $data['vars']; + $this->staticPrefix = $data['path_prefix']; + $this->regex = $data['path_regex']; + $this->tokens = $data['path_tokens']; + $this->pathVariables = $data['path_vars']; + $this->hostRegex = $data['host_regex']; + $this->hostTokens = $data['host_tokens']; + $this->hostVariables = $data['host_vars']; + } + + /** + * Returns the static prefix. + * + * @return string The static prefix + */ + public function getStaticPrefix() + { + return $this->staticPrefix; + } + + /** + * Returns the regex. + * + * @return string The regex + */ + public function getRegex() + { + return $this->regex; + } + + /** + * Returns the host regex. + * + * @return string|null The host regex or null + */ + public function getHostRegex() + { + return $this->hostRegex; + } + + /** + * Returns the tokens. + * + * @return array The tokens + */ + public function getTokens() + { + return $this->tokens; + } + + /** + * Returns the host tokens. + * + * @return array The tokens + */ + public function getHostTokens() + { + return $this->hostTokens; + } + + /** + * Returns the variables. + * + * @return array The variables + */ + public function getVariables() + { + return $this->variables; + } + + /** + * Returns the path variables. + * + * @return array The variables + */ + public function getPathVariables() + { + return $this->pathVariables; + } + + /** + * Returns the host variables. + * + * @return array The variables + */ + public function getHostVariables() + { + return $this->hostVariables; + } +} diff --git a/lib/silex/vendor/symfony/routing/DependencyInjection/RoutingResolverPass.php b/lib/silex/vendor/symfony/routing/DependencyInjection/RoutingResolverPass.php new file mode 100644 index 000000000..4af0a5a28 --- /dev/null +++ b/lib/silex/vendor/symfony/routing/DependencyInjection/RoutingResolverPass.php @@ -0,0 +1,49 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Routing\DependencyInjection; + +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\Compiler\PriorityTaggedServiceTrait; + +/** + * Adds tagged routing.loader services to routing.resolver service. + * + * @author Fabien Potencier + */ +class RoutingResolverPass implements CompilerPassInterface +{ + use PriorityTaggedServiceTrait; + + private $resolverServiceId; + private $loaderTag; + + public function __construct($resolverServiceId = 'routing.resolver', $loaderTag = 'routing.loader') + { + $this->resolverServiceId = $resolverServiceId; + $this->loaderTag = $loaderTag; + } + + public function process(ContainerBuilder $container) + { + if (false === $container->hasDefinition($this->resolverServiceId)) { + return; + } + + $definition = $container->getDefinition($this->resolverServiceId); + + foreach ($this->findAndSortTaggedServices($this->loaderTag, $container) as $id) { + $definition->addMethodCall('addLoader', array(new Reference($id))); + } + } +} diff --git a/lib/silex/vendor/symfony/routing/Exception/ExceptionInterface.php b/lib/silex/vendor/symfony/routing/Exception/ExceptionInterface.php new file mode 100644 index 000000000..db7636211 --- /dev/null +++ b/lib/silex/vendor/symfony/routing/Exception/ExceptionInterface.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\Routing\Exception; + +/** + * ExceptionInterface. + * + * @author Alexandre Salomé + */ +interface ExceptionInterface +{ +} diff --git a/lib/silex/vendor/symfony/routing/Exception/InvalidParameterException.php b/lib/silex/vendor/symfony/routing/Exception/InvalidParameterException.php new file mode 100644 index 000000000..94d841f4c --- /dev/null +++ b/lib/silex/vendor/symfony/routing/Exception/InvalidParameterException.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\Routing\Exception; + +/** + * Exception thrown when a parameter is not valid. + * + * @author Alexandre Salomé + */ +class InvalidParameterException extends \InvalidArgumentException implements ExceptionInterface +{ +} diff --git a/lib/silex/vendor/symfony/routing/Exception/MethodNotAllowedException.php b/lib/silex/vendor/symfony/routing/Exception/MethodNotAllowedException.php new file mode 100644 index 000000000..712412fec --- /dev/null +++ b/lib/silex/vendor/symfony/routing/Exception/MethodNotAllowedException.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\Routing\Exception; + +/** + * The resource was found but the request method is not allowed. + * + * This exception should trigger an HTTP 405 response in your application code. + * + * @author Kris Wallsmith + */ +class MethodNotAllowedException extends \RuntimeException implements ExceptionInterface +{ + protected $allowedMethods = array(); + + public function __construct(array $allowedMethods, $message = null, $code = 0, \Exception $previous = null) + { + $this->allowedMethods = array_map('strtoupper', $allowedMethods); + + parent::__construct($message, $code, $previous); + } + + /** + * Gets the allowed HTTP methods. + * + * @return array + */ + public function getAllowedMethods() + { + return $this->allowedMethods; + } +} diff --git a/lib/silex/vendor/symfony/routing/Exception/MissingMandatoryParametersException.php b/lib/silex/vendor/symfony/routing/Exception/MissingMandatoryParametersException.php new file mode 100644 index 000000000..57f3a40df --- /dev/null +++ b/lib/silex/vendor/symfony/routing/Exception/MissingMandatoryParametersException.php @@ -0,0 +1,22 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Routing\Exception; + +/** + * Exception thrown when a route cannot be generated because of missing + * mandatory parameters. + * + * @author Alexandre Salomé + */ +class MissingMandatoryParametersException extends \InvalidArgumentException implements ExceptionInterface +{ +} diff --git a/lib/silex/vendor/symfony/routing/Exception/NoConfigurationException.php b/lib/silex/vendor/symfony/routing/Exception/NoConfigurationException.php new file mode 100644 index 000000000..333bc7433 --- /dev/null +++ b/lib/silex/vendor/symfony/routing/Exception/NoConfigurationException.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\Routing\Exception; + +/** + * Exception thrown when no routes are configured. + * + * @author Yonel Ceruto + */ +class NoConfigurationException extends ResourceNotFoundException +{ +} diff --git a/lib/silex/vendor/symfony/routing/Exception/ResourceNotFoundException.php b/lib/silex/vendor/symfony/routing/Exception/ResourceNotFoundException.php new file mode 100644 index 000000000..ccbca1527 --- /dev/null +++ b/lib/silex/vendor/symfony/routing/Exception/ResourceNotFoundException.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\Routing\Exception; + +/** + * The resource was not found. + * + * This exception should trigger an HTTP 404 response in your application code. + * + * @author Kris Wallsmith + */ +class ResourceNotFoundException extends \RuntimeException implements ExceptionInterface +{ +} diff --git a/lib/silex/vendor/symfony/routing/Exception/RouteNotFoundException.php b/lib/silex/vendor/symfony/routing/Exception/RouteNotFoundException.php new file mode 100644 index 000000000..24ab0b44a --- /dev/null +++ b/lib/silex/vendor/symfony/routing/Exception/RouteNotFoundException.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\Routing\Exception; + +/** + * Exception thrown when a route does not exist. + * + * @author Alexandre Salomé + */ +class RouteNotFoundException extends \InvalidArgumentException implements ExceptionInterface +{ +} diff --git a/lib/silex/vendor/symfony/routing/Generator/ConfigurableRequirementsInterface.php b/lib/silex/vendor/symfony/routing/Generator/ConfigurableRequirementsInterface.php new file mode 100644 index 000000000..dc97b7e72 --- /dev/null +++ b/lib/silex/vendor/symfony/routing/Generator/ConfigurableRequirementsInterface.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\Routing\Generator; + +/** + * ConfigurableRequirementsInterface must be implemented by URL generators that + * can be configured whether an exception should be generated when the parameters + * do not match the requirements. It is also possible to disable the requirements + * check for URL generation completely. + * + * The possible configurations and use-cases: + * - setStrictRequirements(true): Throw an exception for mismatching requirements. This + * is mostly useful in development environment. + * - setStrictRequirements(false): Don't throw an exception but return null as URL for + * mismatching requirements and log the problem. Useful when you cannot control all + * params because they come from third party libs but don't want to have a 404 in + * production environment. It should log the mismatch so one can review it. + * - setStrictRequirements(null): Return the URL with the given parameters without + * checking the requirements at all. When generating a URL you should either trust + * your params or you validated them beforehand because otherwise it would break your + * link anyway. So in production environment you should know that params always pass + * the requirements. Thus this option allows to disable the check on URL generation for + * performance reasons (saving a preg_match for each requirement every time a URL is + * generated). + * + * @author Fabien Potencier + * @author Tobias Schultze + */ +interface ConfigurableRequirementsInterface +{ + /** + * Enables or disables the exception on incorrect parameters. + * Passing null will deactivate the requirements check completely. + * + * @param bool|null $enabled + */ + public function setStrictRequirements($enabled); + + /** + * Returns whether to throw an exception on incorrect parameters. + * Null means the requirements check is deactivated completely. + * + * @return bool|null + */ + public function isStrictRequirements(); +} diff --git a/lib/silex/vendor/symfony/routing/Generator/Dumper/GeneratorDumper.php b/lib/silex/vendor/symfony/routing/Generator/Dumper/GeneratorDumper.php new file mode 100644 index 000000000..659c5ba1c --- /dev/null +++ b/lib/silex/vendor/symfony/routing/Generator/Dumper/GeneratorDumper.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\Routing\Generator\Dumper; + +use Symfony\Component\Routing\RouteCollection; + +/** + * GeneratorDumper is the base class for all built-in generator dumpers. + * + * @author Fabien Potencier + */ +abstract class GeneratorDumper implements GeneratorDumperInterface +{ + private $routes; + + public function __construct(RouteCollection $routes) + { + $this->routes = $routes; + } + + /** + * {@inheritdoc} + */ + public function getRoutes() + { + return $this->routes; + } +} diff --git a/lib/silex/vendor/symfony/routing/Generator/Dumper/GeneratorDumperInterface.php b/lib/silex/vendor/symfony/routing/Generator/Dumper/GeneratorDumperInterface.php new file mode 100644 index 000000000..fed347239 --- /dev/null +++ b/lib/silex/vendor/symfony/routing/Generator/Dumper/GeneratorDumperInterface.php @@ -0,0 +1,39 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Routing\Generator\Dumper; + +use Symfony\Component\Routing\RouteCollection; + +/** + * GeneratorDumperInterface is the interface that all generator dumper classes must implement. + * + * @author Fabien Potencier + */ +interface GeneratorDumperInterface +{ + /** + * Dumps a set of routes to a string representation of executable code + * that can then be used to generate a URL of such a route. + * + * @param array $options An array of options + * + * @return string Executable code + */ + public function dump(array $options = array()); + + /** + * Gets the routes to dump. + * + * @return RouteCollection A RouteCollection instance + */ + public function getRoutes(); +} diff --git a/lib/silex/vendor/symfony/routing/Generator/Dumper/PhpGeneratorDumper.php b/lib/silex/vendor/symfony/routing/Generator/Dumper/PhpGeneratorDumper.php new file mode 100644 index 000000000..60bdf1da3 --- /dev/null +++ b/lib/silex/vendor/symfony/routing/Generator/Dumper/PhpGeneratorDumper.php @@ -0,0 +1,118 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Routing\Generator\Dumper; + +/** + * PhpGeneratorDumper creates a PHP class able to generate URLs for a given set of routes. + * + * @author Fabien Potencier + * @author Tobias Schultze + */ +class PhpGeneratorDumper extends GeneratorDumper +{ + /** + * Dumps a set of routes to a PHP class. + * + * Available options: + * + * * class: The class name + * * base_class: The base class name + * + * @param array $options An array of options + * + * @return string A PHP class representing the generator class + */ + public function dump(array $options = array()) + { + $options = array_merge(array( + 'class' => 'ProjectUrlGenerator', + 'base_class' => 'Symfony\\Component\\Routing\\Generator\\UrlGenerator', + ), $options); + + return <<context = \$context; + \$this->logger = \$logger; + if (null === self::\$declaredRoutes) { + self::\$declaredRoutes = {$this->generateDeclaredRoutes()}; + } + } + +{$this->generateGenerateMethod()} +} + +EOF; + } + + /** + * Generates PHP code representing an array of defined routes + * together with the routes properties (e.g. requirements). + * + * @return string PHP code + */ + private function generateDeclaredRoutes() + { + $routes = "array(\n"; + foreach ($this->getRoutes()->all() as $name => $route) { + $compiledRoute = $route->compile(); + + $properties = array(); + $properties[] = $compiledRoute->getVariables(); + $properties[] = $route->getDefaults(); + $properties[] = $route->getRequirements(); + $properties[] = $compiledRoute->getTokens(); + $properties[] = $compiledRoute->getHostTokens(); + $properties[] = $route->getSchemes(); + + $routes .= sprintf(" '%s' => %s,\n", $name, str_replace("\n", '', var_export($properties, true))); + } + $routes .= ' )'; + + return $routes; + } + + /** + * Generates PHP code representing the `generate` method that implements the UrlGeneratorInterface. + * + * @return string PHP code + */ + private function generateGenerateMethod() + { + return <<<'EOF' + public function generate($name, $parameters = array(), $referenceType = self::ABSOLUTE_PATH) + { + if (!isset(self::$declaredRoutes[$name])) { + throw new RouteNotFoundException(sprintf('Unable to generate a URL for the named route "%s" as such route does not exist.', $name)); + } + + list($variables, $defaults, $requirements, $tokens, $hostTokens, $requiredSchemes) = self::$declaredRoutes[$name]; + + return $this->doGenerate($variables, $defaults, $requirements, $tokens, $parameters, $name, $referenceType, $hostTokens, $requiredSchemes); + } +EOF; + } +} diff --git a/lib/silex/vendor/symfony/routing/Generator/UrlGenerator.php b/lib/silex/vendor/symfony/routing/Generator/UrlGenerator.php new file mode 100644 index 000000000..02a59a925 --- /dev/null +++ b/lib/silex/vendor/symfony/routing/Generator/UrlGenerator.php @@ -0,0 +1,321 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Routing\Generator; + +use Symfony\Component\Routing\RouteCollection; +use Symfony\Component\Routing\RequestContext; +use Symfony\Component\Routing\Exception\InvalidParameterException; +use Symfony\Component\Routing\Exception\RouteNotFoundException; +use Symfony\Component\Routing\Exception\MissingMandatoryParametersException; +use Psr\Log\LoggerInterface; + +/** + * UrlGenerator can generate a URL or a path for any route in the RouteCollection + * based on the passed parameters. + * + * @author Fabien Potencier + * @author Tobias Schultze + */ +class UrlGenerator implements UrlGeneratorInterface, ConfigurableRequirementsInterface +{ + protected $routes; + protected $context; + + /** + * @var bool|null + */ + protected $strictRequirements = true; + + protected $logger; + + /** + * This array defines the characters (besides alphanumeric ones) that will not be percent-encoded in the path segment of the generated URL. + * + * PHP's rawurlencode() encodes all chars except "a-zA-Z0-9-._~" according to RFC 3986. But we want to allow some chars + * to be used in their literal form (reasons below). Other chars inside the path must of course be encoded, e.g. + * "?" and "#" (would be interpreted wrongly as query and fragment identifier), + * "'" and """ (are used as delimiters in HTML). + */ + protected $decodedChars = array( + // the slash can be used to designate a hierarchical structure and we want allow using it with this meaning + // some webservers don't allow the slash in encoded form in the path for security reasons anyway + // see http://stackoverflow.com/questions/4069002/http-400-if-2f-part-of-get-url-in-jboss + '%2F' => '/', + // the following chars are general delimiters in the URI specification but have only special meaning in the authority component + // so they can safely be used in the path in unencoded form + '%40' => '@', + '%3A' => ':', + // these chars are only sub-delimiters that have no predefined meaning and can therefore be used literally + // so URI producing applications can use these chars to delimit subcomponents in a path segment without being encoded for better readability + '%3B' => ';', + '%2C' => ',', + '%3D' => '=', + '%2B' => '+', + '%21' => '!', + '%2A' => '*', + '%7C' => '|', + ); + + public function __construct(RouteCollection $routes, RequestContext $context, LoggerInterface $logger = null) + { + $this->routes = $routes; + $this->context = $context; + $this->logger = $logger; + } + + /** + * {@inheritdoc} + */ + public function setContext(RequestContext $context) + { + $this->context = $context; + } + + /** + * {@inheritdoc} + */ + public function getContext() + { + return $this->context; + } + + /** + * {@inheritdoc} + */ + public function setStrictRequirements($enabled) + { + $this->strictRequirements = null === $enabled ? null : (bool) $enabled; + } + + /** + * {@inheritdoc} + */ + public function isStrictRequirements() + { + return $this->strictRequirements; + } + + /** + * {@inheritdoc} + */ + public function generate($name, $parameters = array(), $referenceType = self::ABSOLUTE_PATH) + { + if (null === $route = $this->routes->get($name)) { + throw new RouteNotFoundException(sprintf('Unable to generate a URL for the named route "%s" as such route does not exist.', $name)); + } + + // the Route has a cache of its own and is not recompiled as long as it does not get modified + $compiledRoute = $route->compile(); + + return $this->doGenerate($compiledRoute->getVariables(), $route->getDefaults(), $route->getRequirements(), $compiledRoute->getTokens(), $parameters, $name, $referenceType, $compiledRoute->getHostTokens(), $route->getSchemes()); + } + + /** + * @throws MissingMandatoryParametersException When some parameters are missing that are mandatory for the route + * @throws InvalidParameterException When a parameter value for a placeholder is not correct because + * it does not match the requirement + */ + protected function doGenerate($variables, $defaults, $requirements, $tokens, $parameters, $name, $referenceType, $hostTokens, array $requiredSchemes = array()) + { + $variables = array_flip($variables); + $mergedParams = array_replace($defaults, $this->context->getParameters(), $parameters); + + // all params must be given + if ($diff = array_diff_key($variables, $mergedParams)) { + throw new MissingMandatoryParametersException(sprintf('Some mandatory parameters are missing ("%s") to generate a URL for route "%s".', implode('", "', array_keys($diff)), $name)); + } + + $url = ''; + $optional = true; + $message = 'Parameter "{parameter}" for route "{route}" must match "{expected}" ("{given}" given) to generate a corresponding URL.'; + foreach ($tokens as $token) { + if ('variable' === $token[0]) { + if (!$optional || !array_key_exists($token[3], $defaults) || null !== $mergedParams[$token[3]] && (string) $mergedParams[$token[3]] !== (string) $defaults[$token[3]]) { + // check requirement + if (null !== $this->strictRequirements && !preg_match('#^'.$token[2].'$#'.(empty($token[4]) ? '' : 'u'), $mergedParams[$token[3]])) { + if ($this->strictRequirements) { + throw new InvalidParameterException(strtr($message, array('{parameter}' => $token[3], '{route}' => $name, '{expected}' => $token[2], '{given}' => $mergedParams[$token[3]]))); + } + + if ($this->logger) { + $this->logger->error($message, array('parameter' => $token[3], 'route' => $name, 'expected' => $token[2], 'given' => $mergedParams[$token[3]])); + } + + return; + } + + $url = $token[1].$mergedParams[$token[3]].$url; + $optional = false; + } + } else { + // static text + $url = $token[1].$url; + $optional = false; + } + } + + if ('' === $url) { + $url = '/'; + } + + // the contexts base URL is already encoded (see Symfony\Component\HttpFoundation\Request) + $url = strtr(rawurlencode($url), $this->decodedChars); + + // the path segments "." and ".." are interpreted as relative reference when resolving a URI; see http://tools.ietf.org/html/rfc3986#section-3.3 + // so we need to encode them as they are not used for this purpose here + // otherwise we would generate a URI that, when followed by a user agent (e.g. browser), does not match this route + $url = strtr($url, array('/../' => '/%2E%2E/', '/./' => '/%2E/')); + if ('/..' === substr($url, -3)) { + $url = substr($url, 0, -2).'%2E%2E'; + } elseif ('/.' === substr($url, -2)) { + $url = substr($url, 0, -1).'%2E'; + } + + $schemeAuthority = ''; + $host = $this->context->getHost(); + $scheme = $this->context->getScheme(); + + if ($requiredSchemes) { + if (!in_array($scheme, $requiredSchemes, true)) { + $referenceType = self::ABSOLUTE_URL; + $scheme = current($requiredSchemes); + } + } + + if ($hostTokens) { + $routeHost = ''; + foreach ($hostTokens as $token) { + if ('variable' === $token[0]) { + if (null !== $this->strictRequirements && !preg_match('#^'.$token[2].'$#i'.(empty($token[4]) ? '' : 'u'), $mergedParams[$token[3]])) { + if ($this->strictRequirements) { + throw new InvalidParameterException(strtr($message, array('{parameter}' => $token[3], '{route}' => $name, '{expected}' => $token[2], '{given}' => $mergedParams[$token[3]]))); + } + + if ($this->logger) { + $this->logger->error($message, array('parameter' => $token[3], 'route' => $name, 'expected' => $token[2], 'given' => $mergedParams[$token[3]])); + } + + return; + } + + $routeHost = $token[1].$mergedParams[$token[3]].$routeHost; + } else { + $routeHost = $token[1].$routeHost; + } + } + + if ($routeHost !== $host) { + $host = $routeHost; + if (self::ABSOLUTE_URL !== $referenceType) { + $referenceType = self::NETWORK_PATH; + } + } + } + + if ((self::ABSOLUTE_URL === $referenceType || self::NETWORK_PATH === $referenceType) && !empty($host)) { + $port = ''; + if ('http' === $scheme && 80 != $this->context->getHttpPort()) { + $port = ':'.$this->context->getHttpPort(); + } elseif ('https' === $scheme && 443 != $this->context->getHttpsPort()) { + $port = ':'.$this->context->getHttpsPort(); + } + + $schemeAuthority = self::NETWORK_PATH === $referenceType ? '//' : "$scheme://"; + $schemeAuthority .= $host.$port; + } + + if (self::RELATIVE_PATH === $referenceType) { + $url = self::getRelativePath($this->context->getPathInfo(), $url); + } else { + $url = $schemeAuthority.$this->context->getBaseUrl().$url; + } + + // add a query string if needed + $extra = array_udiff_assoc(array_diff_key($parameters, $variables), $defaults, function ($a, $b) { + return $a == $b ? 0 : 1; + }); + + // extract fragment + $fragment = ''; + if (isset($defaults['_fragment'])) { + $fragment = $defaults['_fragment']; + } + + if (isset($extra['_fragment'])) { + $fragment = $extra['_fragment']; + unset($extra['_fragment']); + } + + if ($extra && $query = http_build_query($extra, '', '&', PHP_QUERY_RFC3986)) { + // "/" and "?" can be left decoded for better user experience, see + // http://tools.ietf.org/html/rfc3986#section-3.4 + $url .= '?'.strtr($query, array('%2F' => '/')); + } + + if ('' !== $fragment) { + $url .= '#'.strtr(rawurlencode($fragment), array('%2F' => '/', '%3F' => '?')); + } + + return $url; + } + + /** + * Returns the target path as relative reference from the base path. + * + * Only the URIs path component (no schema, host etc.) is relevant and must be given, starting with a slash. + * Both paths must be absolute and not contain relative parts. + * Relative URLs from one resource to another are useful when generating self-contained downloadable document archives. + * Furthermore, they can be used to reduce the link size in documents. + * + * Example target paths, given a base path of "/a/b/c/d": + * - "/a/b/c/d" -> "" + * - "/a/b/c/" -> "./" + * - "/a/b/" -> "../" + * - "/a/b/c/other" -> "other" + * - "/a/x/y" -> "../../x/y" + * + * @param string $basePath The base path + * @param string $targetPath The target path + * + * @return string The relative target path + */ + public static function getRelativePath($basePath, $targetPath) + { + if ($basePath === $targetPath) { + return ''; + } + + $sourceDirs = explode('/', isset($basePath[0]) && '/' === $basePath[0] ? substr($basePath, 1) : $basePath); + $targetDirs = explode('/', isset($targetPath[0]) && '/' === $targetPath[0] ? substr($targetPath, 1) : $targetPath); + array_pop($sourceDirs); + $targetFile = array_pop($targetDirs); + + foreach ($sourceDirs as $i => $dir) { + if (isset($targetDirs[$i]) && $dir === $targetDirs[$i]) { + unset($sourceDirs[$i], $targetDirs[$i]); + } else { + break; + } + } + + $targetDirs[] = $targetFile; + $path = str_repeat('../', count($sourceDirs)).implode('/', $targetDirs); + + // 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). + return '' === $path || '/' === $path[0] + || false !== ($colonPos = strpos($path, ':')) && ($colonPos < ($slashPos = strpos($path, '/')) || false === $slashPos) + ? "./$path" : $path; + } +} diff --git a/lib/silex/vendor/symfony/routing/Generator/UrlGeneratorInterface.php b/lib/silex/vendor/symfony/routing/Generator/UrlGeneratorInterface.php new file mode 100644 index 000000000..d6e7938e5 --- /dev/null +++ b/lib/silex/vendor/symfony/routing/Generator/UrlGeneratorInterface.php @@ -0,0 +1,86 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Routing\Generator; + +use Symfony\Component\Routing\Exception\InvalidParameterException; +use Symfony\Component\Routing\Exception\MissingMandatoryParametersException; +use Symfony\Component\Routing\Exception\RouteNotFoundException; +use Symfony\Component\Routing\RequestContextAwareInterface; + +/** + * UrlGeneratorInterface is the interface that all URL generator classes must implement. + * + * The constants in this interface define the different types of resource references that + * are declared in RFC 3986: http://tools.ietf.org/html/rfc3986 + * We are using the term "URL" instead of "URI" as this is more common in web applications + * and we do not need to distinguish them as the difference is mostly semantical and + * less technical. Generating URIs, i.e. representation-independent resource identifiers, + * is also possible. + * + * @author Fabien Potencier + * @author Tobias Schultze + */ +interface UrlGeneratorInterface extends RequestContextAwareInterface +{ + /** + * Generates an absolute URL, e.g. "http://example.com/dir/file". + */ + const ABSOLUTE_URL = 0; + + /** + * Generates an absolute path, e.g. "/dir/file". + */ + const ABSOLUTE_PATH = 1; + + /** + * Generates a relative path based on the current request path, e.g. "../parent-file". + * + * @see UrlGenerator::getRelativePath() + */ + const RELATIVE_PATH = 2; + + /** + * Generates a network path, e.g. "//example.com/dir/file". + * Such reference reuses the current scheme but specifies the host. + */ + const NETWORK_PATH = 3; + + /** + * Generates a URL or path for a specific route based on the given parameters. + * + * Parameters that reference placeholders in the route pattern will substitute them in the + * path or host. Extra params are added as query string to the URL. + * + * When the passed reference type cannot be generated for the route because it requires a different + * host or scheme than the current one, the method will return a more comprehensive reference + * that includes the required params. For example, when you call this method with $referenceType = ABSOLUTE_PATH + * but the route requires the https scheme whereas the current scheme is http, it will instead return an + * ABSOLUTE_URL with the https scheme and the current host. This makes sure the generated URL matches + * the route in any case. + * + * If there is no route with the given name, the generator must throw the RouteNotFoundException. + * + * The special parameter _fragment will be used as the document fragment suffixed to the final URL. + * + * @param string $name The name of the route + * @param mixed $parameters An array of parameters + * @param int $referenceType The type of reference to be generated (one of the constants) + * + * @return string The generated URL + * + * @throws RouteNotFoundException If the named route doesn't exist + * @throws MissingMandatoryParametersException When some parameters are missing that are mandatory for the route + * @throws InvalidParameterException When a parameter value for a placeholder is not correct because + * it does not match the requirement + */ + public function generate($name, $parameters = array(), $referenceType = self::ABSOLUTE_PATH); +} diff --git a/lib/silex/vendor/symfony/routing/LICENSE b/lib/silex/vendor/symfony/routing/LICENSE new file mode 100644 index 000000000..21d7fb9e2 --- /dev/null +++ b/lib/silex/vendor/symfony/routing/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2004-2018 Fabien Potencier + +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/lib/silex/vendor/symfony/routing/Loader/AnnotationClassLoader.php b/lib/silex/vendor/symfony/routing/Loader/AnnotationClassLoader.php new file mode 100644 index 000000000..2fe6fb596 --- /dev/null +++ b/lib/silex/vendor/symfony/routing/Loader/AnnotationClassLoader.php @@ -0,0 +1,269 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Routing\Loader; + +use Doctrine\Common\Annotations\Reader; +use Symfony\Component\Config\Resource\FileResource; +use Symfony\Component\Routing\Route; +use Symfony\Component\Routing\RouteCollection; +use Symfony\Component\Config\Loader\LoaderInterface; +use Symfony\Component\Config\Loader\LoaderResolverInterface; + +/** + * AnnotationClassLoader loads routing information from a PHP class and its methods. + * + * You need to define an implementation for the getRouteDefaults() method. Most of the + * time, this method should define some PHP callable to be called for the route + * (a controller in MVC speak). + * + * The @Route annotation can be set on the class (for global parameters), + * and on each method. + * + * The @Route annotation main value is the route path. The annotation also + * recognizes several parameters: requirements, options, defaults, schemes, + * methods, host, and name. The name parameter is mandatory. + * Here is an example of how you should be able to use it: + * + * /** + * * @Route("/Blog") + * * / + * class Blog + * { + * /** + * * @Route("/", name="blog_index") + * * / + * public function index() + * { + * } + * + * /** + * * @Route("/{id}", name="blog_post", requirements = {"id" = "\d+"}) + * * / + * public function show() + * { + * } + * } + * + * @author Fabien Potencier + */ +abstract class AnnotationClassLoader implements LoaderInterface +{ + protected $reader; + + /** + * @var string + */ + protected $routeAnnotationClass = 'Symfony\\Component\\Routing\\Annotation\\Route'; + + /** + * @var int + */ + protected $defaultRouteIndex = 0; + + public function __construct(Reader $reader) + { + $this->reader = $reader; + } + + /** + * Sets the annotation class to read route properties from. + * + * @param string $class A fully-qualified class name + */ + public function setRouteAnnotationClass($class) + { + $this->routeAnnotationClass = $class; + } + + /** + * Loads from annotations from a class. + * + * @param string $class A class name + * @param string|null $type The resource type + * + * @return RouteCollection A RouteCollection instance + * + * @throws \InvalidArgumentException When route can't be parsed + */ + public function load($class, $type = null) + { + if (!class_exists($class)) { + throw new \InvalidArgumentException(sprintf('Class "%s" does not exist.', $class)); + } + + $class = new \ReflectionClass($class); + if ($class->isAbstract()) { + throw new \InvalidArgumentException(sprintf('Annotations from class "%s" cannot be read as it is abstract.', $class->getName())); + } + + $globals = $this->getGlobals($class); + + $collection = new RouteCollection(); + $collection->addResource(new FileResource($class->getFileName())); + + foreach ($class->getMethods() as $method) { + $this->defaultRouteIndex = 0; + foreach ($this->reader->getMethodAnnotations($method) as $annot) { + if ($annot instanceof $this->routeAnnotationClass) { + $this->addRoute($collection, $annot, $globals, $class, $method); + } + } + } + + if (0 === $collection->count() && $class->hasMethod('__invoke') && $annot = $this->reader->getClassAnnotation($class, $this->routeAnnotationClass)) { + $globals['path'] = ''; + $globals['name'] = ''; + $this->addRoute($collection, $annot, $globals, $class, $class->getMethod('__invoke')); + } + + return $collection; + } + + protected function addRoute(RouteCollection $collection, $annot, $globals, \ReflectionClass $class, \ReflectionMethod $method) + { + $name = $annot->getName(); + if (null === $name) { + $name = $this->getDefaultRouteName($class, $method); + } + $name = $globals['name'].$name; + + $defaults = array_replace($globals['defaults'], $annot->getDefaults()); + foreach ($method->getParameters() as $param) { + if (false !== strpos($globals['path'].$annot->getPath(), sprintf('{%s}', $param->getName())) && !isset($defaults[$param->getName()]) && $param->isDefaultValueAvailable()) { + $defaults[$param->getName()] = $param->getDefaultValue(); + } + } + $requirements = array_replace($globals['requirements'], $annot->getRequirements()); + $options = array_replace($globals['options'], $annot->getOptions()); + $schemes = array_merge($globals['schemes'], $annot->getSchemes()); + $methods = array_merge($globals['methods'], $annot->getMethods()); + + $host = $annot->getHost(); + if (null === $host) { + $host = $globals['host']; + } + + $condition = $annot->getCondition(); + if (null === $condition) { + $condition = $globals['condition']; + } + + $route = $this->createRoute($globals['path'].$annot->getPath(), $defaults, $requirements, $options, $host, $schemes, $methods, $condition); + + $this->configureRoute($route, $class, $method, $annot); + + $collection->add($name, $route); + } + + /** + * {@inheritdoc} + */ + public function supports($resource, $type = null) + { + return is_string($resource) && preg_match('/^(?:\\\\?[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)+$/', $resource) && (!$type || 'annotation' === $type); + } + + /** + * {@inheritdoc} + */ + public function setResolver(LoaderResolverInterface $resolver) + { + } + + /** + * {@inheritdoc} + */ + public function getResolver() + { + } + + /** + * Gets the default route name for a class method. + * + * @param \ReflectionClass $class + * @param \ReflectionMethod $method + * + * @return string + */ + protected function getDefaultRouteName(\ReflectionClass $class, \ReflectionMethod $method) + { + $name = strtolower(str_replace('\\', '_', $class->name).'_'.$method->name); + if ($this->defaultRouteIndex > 0) { + $name .= '_'.$this->defaultRouteIndex; + } + ++$this->defaultRouteIndex; + + return $name; + } + + protected function getGlobals(\ReflectionClass $class) + { + $globals = array( + 'path' => '', + 'requirements' => array(), + 'options' => array(), + 'defaults' => array(), + 'schemes' => array(), + 'methods' => array(), + 'host' => '', + 'condition' => '', + 'name' => '', + ); + + if ($annot = $this->reader->getClassAnnotation($class, $this->routeAnnotationClass)) { + if (null !== $annot->getName()) { + $globals['name'] = $annot->getName(); + } + + if (null !== $annot->getPath()) { + $globals['path'] = $annot->getPath(); + } + + if (null !== $annot->getRequirements()) { + $globals['requirements'] = $annot->getRequirements(); + } + + if (null !== $annot->getOptions()) { + $globals['options'] = $annot->getOptions(); + } + + if (null !== $annot->getDefaults()) { + $globals['defaults'] = $annot->getDefaults(); + } + + if (null !== $annot->getSchemes()) { + $globals['schemes'] = $annot->getSchemes(); + } + + if (null !== $annot->getMethods()) { + $globals['methods'] = $annot->getMethods(); + } + + if (null !== $annot->getHost()) { + $globals['host'] = $annot->getHost(); + } + + if (null !== $annot->getCondition()) { + $globals['condition'] = $annot->getCondition(); + } + } + + return $globals; + } + + protected function createRoute($path, $defaults, $requirements, $options, $host, $schemes, $methods, $condition) + { + return new Route($path, $defaults, $requirements, $options, $host, $schemes, $methods, $condition); + } + + abstract protected function configureRoute(Route $route, \ReflectionClass $class, \ReflectionMethod $method, $annot); +} diff --git a/lib/silex/vendor/symfony/routing/Loader/AnnotationDirectoryLoader.php b/lib/silex/vendor/symfony/routing/Loader/AnnotationDirectoryLoader.php new file mode 100644 index 000000000..4574a0201 --- /dev/null +++ b/lib/silex/vendor/symfony/routing/Loader/AnnotationDirectoryLoader.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\Routing\Loader; + +use Symfony\Component\Routing\RouteCollection; +use Symfony\Component\Config\Resource\DirectoryResource; + +/** + * AnnotationDirectoryLoader loads routing information from annotations set + * on PHP classes and methods. + * + * @author Fabien Potencier + */ +class AnnotationDirectoryLoader extends AnnotationFileLoader +{ + /** + * Loads from annotations from a directory. + * + * @param string $path A directory path + * @param string|null $type The resource type + * + * @return RouteCollection A RouteCollection instance + * + * @throws \InvalidArgumentException When the directory does not exist or its routes cannot be parsed + */ + public function load($path, $type = null) + { + if (!is_dir($dir = $this->locator->locate($path))) { + return parent::supports($path, $type) ? parent::load($path, $type) : new RouteCollection(); + } + + $collection = new RouteCollection(); + $collection->addResource(new DirectoryResource($dir, '/\.php$/')); + $files = iterator_to_array(new \RecursiveIteratorIterator( + new \RecursiveCallbackFilterIterator( + new \RecursiveDirectoryIterator($dir, \FilesystemIterator::SKIP_DOTS | \FilesystemIterator::FOLLOW_SYMLINKS), + function (\SplFileInfo $current) { + return '.' !== substr($current->getBasename(), 0, 1); + } + ), + \RecursiveIteratorIterator::LEAVES_ONLY + )); + usort($files, function (\SplFileInfo $a, \SplFileInfo $b) { + return (string) $a > (string) $b ? 1 : -1; + }); + + foreach ($files as $file) { + if (!$file->isFile() || '.php' !== substr($file->getFilename(), -4)) { + continue; + } + + if ($class = $this->findClass($file)) { + $refl = new \ReflectionClass($class); + if ($refl->isAbstract()) { + continue; + } + + $collection->addCollection($this->loader->load($class, $type)); + } + } + + return $collection; + } + + /** + * {@inheritdoc} + */ + public function supports($resource, $type = null) + { + if ('annotation' === $type) { + return true; + } + + if ($type || !is_string($resource)) { + return false; + } + + try { + return is_dir($this->locator->locate($resource)); + } catch (\Exception $e) { + return false; + } + } +} diff --git a/lib/silex/vendor/symfony/routing/Loader/AnnotationFileLoader.php b/lib/silex/vendor/symfony/routing/Loader/AnnotationFileLoader.php new file mode 100644 index 000000000..cf9f0704c --- /dev/null +++ b/lib/silex/vendor/symfony/routing/Loader/AnnotationFileLoader.php @@ -0,0 +1,142 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Routing\Loader; + +use Symfony\Component\Routing\RouteCollection; +use Symfony\Component\Config\Resource\FileResource; +use Symfony\Component\Config\Loader\FileLoader; +use Symfony\Component\Config\FileLocatorInterface; + +/** + * AnnotationFileLoader loads routing information from annotations set + * on a PHP class and its methods. + * + * @author Fabien Potencier + */ +class AnnotationFileLoader extends FileLoader +{ + protected $loader; + + /** + * @throws \RuntimeException + */ + public function __construct(FileLocatorInterface $locator, AnnotationClassLoader $loader) + { + if (!function_exists('token_get_all')) { + throw new \RuntimeException('The Tokenizer extension is required for the routing annotation loaders.'); + } + + parent::__construct($locator); + + $this->loader = $loader; + } + + /** + * Loads from annotations from a file. + * + * @param string $file A PHP file path + * @param string|null $type The resource type + * + * @return RouteCollection A RouteCollection instance + * + * @throws \InvalidArgumentException When the file does not exist or its routes cannot be parsed + */ + public function load($file, $type = null) + { + $path = $this->locator->locate($file); + + $collection = new RouteCollection(); + if ($class = $this->findClass($path)) { + $collection->addResource(new FileResource($path)); + $collection->addCollection($this->loader->load($class, $type)); + } + if (\PHP_VERSION_ID >= 70000) { + // PHP 7 memory manager will not release after token_get_all(), see https://bugs.php.net/70098 + gc_mem_caches(); + } + + return $collection; + } + + /** + * {@inheritdoc} + */ + public function supports($resource, $type = null) + { + return is_string($resource) && 'php' === pathinfo($resource, PATHINFO_EXTENSION) && (!$type || 'annotation' === $type); + } + + /** + * Returns the full class name for the first class in the file. + * + * @param string $file A PHP file path + * + * @return string|false Full class name if found, false otherwise + */ + protected function findClass($file) + { + $class = false; + $namespace = false; + $tokens = token_get_all(file_get_contents($file)); + + if (1 === count($tokens) && T_INLINE_HTML === $tokens[0][0]) { + throw new \InvalidArgumentException(sprintf('The file "%s" does not contain PHP code. Did you forgot to add the " 0; --$j) { + if (!isset($tokens[$j][1])) { + break; + } + + if (T_DOUBLE_COLON === $tokens[$j][0] || T_NEW === $tokens[$j][0]) { + $skipClassToken = true; + break; + } elseif (!in_array($tokens[$j][0], array(T_WHITESPACE, T_DOC_COMMENT, T_COMMENT))) { + break; + } + } + + if (!$skipClassToken) { + $class = true; + } + } + + if (T_NAMESPACE === $token[0]) { + $namespace = true; + } + } + + return false; + } +} diff --git a/lib/silex/vendor/symfony/routing/Loader/ClosureLoader.php b/lib/silex/vendor/symfony/routing/Loader/ClosureLoader.php new file mode 100644 index 000000000..5df9f6ae8 --- /dev/null +++ b/lib/silex/vendor/symfony/routing/Loader/ClosureLoader.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\Routing\Loader; + +use Symfony\Component\Config\Loader\Loader; +use Symfony\Component\Routing\RouteCollection; + +/** + * ClosureLoader loads routes from a PHP closure. + * + * The Closure must return a RouteCollection instance. + * + * @author Fabien Potencier + */ +class ClosureLoader extends Loader +{ + /** + * Loads a Closure. + * + * @param \Closure $closure A Closure + * @param string|null $type The resource type + * + * @return RouteCollection A RouteCollection instance + */ + public function load($closure, $type = null) + { + return $closure(); + } + + /** + * {@inheritdoc} + */ + public function supports($resource, $type = null) + { + return $resource instanceof \Closure && (!$type || 'closure' === $type); + } +} diff --git a/lib/silex/vendor/symfony/routing/Loader/Configurator/CollectionConfigurator.php b/lib/silex/vendor/symfony/routing/Loader/Configurator/CollectionConfigurator.php new file mode 100644 index 000000000..38d86cb89 --- /dev/null +++ b/lib/silex/vendor/symfony/routing/Loader/Configurator/CollectionConfigurator.php @@ -0,0 +1,79 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Routing\Loader\Configurator; + +use Symfony\Component\Routing\Route; +use Symfony\Component\Routing\RouteCollection; + +/** + * @author Nicolas Grekas + */ +class CollectionConfigurator +{ + use Traits\AddTrait; + use Traits\RouteTrait; + + private $parent; + + public function __construct(RouteCollection $parent, $name) + { + $this->parent = $parent; + $this->name = $name; + $this->collection = new RouteCollection(); + $this->route = new Route(''); + } + + public function __destruct() + { + $this->collection->addPrefix(rtrim($this->route->getPath(), '/')); + $this->parent->addCollection($this->collection); + } + + /** + * Adds a route. + * + * @param string $name + * @param string $path + * + * @return RouteConfigurator + */ + final public function add($name, $path) + { + $this->collection->add($this->name.$name, $route = clone $this->route); + + return new RouteConfigurator($this->collection, $route->setPath($path), $this->name); + } + + /** + * Creates a sub-collection. + * + * @return self + */ + final public function collection($name = '') + { + return new self($this->collection, $this->name.$name); + } + + /** + * Sets the prefix to add to the path of all child routes. + * + * @param string $prefix + * + * @return $this + */ + final public function prefix($prefix) + { + $this->route->setPath($prefix); + + return $this; + } +} diff --git a/lib/silex/vendor/symfony/routing/Loader/Configurator/ImportConfigurator.php b/lib/silex/vendor/symfony/routing/Loader/Configurator/ImportConfigurator.php new file mode 100644 index 000000000..d0a3c373f --- /dev/null +++ b/lib/silex/vendor/symfony/routing/Loader/Configurator/ImportConfigurator.php @@ -0,0 +1,49 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Routing\Loader\Configurator; + +use Symfony\Component\Routing\RouteCollection; + +/** + * @author Nicolas Grekas + */ +class ImportConfigurator +{ + use Traits\RouteTrait; + + private $parent; + + public function __construct(RouteCollection $parent, RouteCollection $route) + { + $this->parent = $parent; + $this->route = $route; + } + + public function __destruct() + { + $this->parent->addCollection($this->route); + } + + /** + * Sets the prefix to add to the path of all child routes. + * + * @param string $prefix + * + * @return $this + */ + final public function prefix($prefix) + { + $this->route->addPrefix($prefix); + + return $this; + } +} diff --git a/lib/silex/vendor/symfony/routing/Loader/Configurator/RouteConfigurator.php b/lib/silex/vendor/symfony/routing/Loader/Configurator/RouteConfigurator.php new file mode 100644 index 000000000..b8d870254 --- /dev/null +++ b/lib/silex/vendor/symfony/routing/Loader/Configurator/RouteConfigurator.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\Routing\Loader\Configurator; + +use Symfony\Component\Routing\Route; +use Symfony\Component\Routing\RouteCollection; + +/** + * @author Nicolas Grekas + */ +class RouteConfigurator +{ + use Traits\AddTrait; + use Traits\RouteTrait; + + public function __construct(RouteCollection $collection, Route $route, $name = '') + { + $this->collection = $collection; + $this->route = $route; + $this->name = $name; + } +} diff --git a/lib/silex/vendor/symfony/routing/Loader/Configurator/RoutingConfigurator.php b/lib/silex/vendor/symfony/routing/Loader/Configurator/RoutingConfigurator.php new file mode 100644 index 000000000..4591a86ba --- /dev/null +++ b/lib/silex/vendor/symfony/routing/Loader/Configurator/RoutingConfigurator.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\Routing\Loader\Configurator; + +use Symfony\Component\Routing\Loader\PhpFileLoader; +use Symfony\Component\Routing\RouteCollection; + +/** + * @author Nicolas Grekas + */ +class RoutingConfigurator +{ + use Traits\AddTrait; + + private $loader; + private $path; + private $file; + + public function __construct(RouteCollection $collection, PhpFileLoader $loader, $path, $file) + { + $this->collection = $collection; + $this->loader = $loader; + $this->path = $path; + $this->file = $file; + } + + /** + * @return ImportConfigurator + */ + final public function import($resource, $type = null, $ignoreErrors = false) + { + $this->loader->setCurrentDir(dirname($this->path)); + $subCollection = $this->loader->import($resource, $type, $ignoreErrors, $this->file); + + return new ImportConfigurator($this->collection, $subCollection); + } + + /** + * @return CollectionConfigurator + */ + final public function collection($name = '') + { + return new CollectionConfigurator($this->collection, $name); + } +} diff --git a/lib/silex/vendor/symfony/routing/Loader/Configurator/Traits/AddTrait.php b/lib/silex/vendor/symfony/routing/Loader/Configurator/Traits/AddTrait.php new file mode 100644 index 000000000..7171fd241 --- /dev/null +++ b/lib/silex/vendor/symfony/routing/Loader/Configurator/Traits/AddTrait.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\Routing\Loader\Configurator\Traits; + +use Symfony\Component\Routing\Loader\Configurator\RouteConfigurator; +use Symfony\Component\Routing\Route; +use Symfony\Component\Routing\RouteCollection; + +trait AddTrait +{ + /** + * @var RouteCollection + */ + private $collection; + + private $name = ''; + + /** + * Adds a route. + * + * @param string $name + * @param string $path + * + * @return RouteConfigurator + */ + final public function add($name, $path) + { + $this->collection->add($this->name.$name, $route = new Route($path)); + + return new RouteConfigurator($this->collection, $route); + } + + /** + * Adds a route. + * + * @param string $name + * @param string $path + * + * @return RouteConfigurator + */ + final public function __invoke($name, $path) + { + return $this->add($name, $path); + } +} diff --git a/lib/silex/vendor/symfony/routing/Loader/Configurator/Traits/RouteTrait.php b/lib/silex/vendor/symfony/routing/Loader/Configurator/Traits/RouteTrait.php new file mode 100644 index 000000000..4d2e255b1 --- /dev/null +++ b/lib/silex/vendor/symfony/routing/Loader/Configurator/Traits/RouteTrait.php @@ -0,0 +1,131 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Routing\Loader\Configurator\Traits; + +use Symfony\Component\Routing\Route; +use Symfony\Component\Routing\RouteCollection; + +trait RouteTrait +{ + /** + * @var RouteCollection|Route + */ + private $route; + + /** + * Adds defaults. + * + * @return $this + */ + final public function defaults(array $defaults) + { + $this->route->addDefaults($defaults); + + return $this; + } + + /** + * Adds requirements. + * + * @return $this + */ + final public function requirements(array $requirements) + { + $this->route->addRequirements($requirements); + + return $this; + } + + /** + * Adds options. + * + * @return $this + */ + final public function options(array $options) + { + $this->route->addOptions($options); + + return $this; + } + + /** + * Sets the condition. + * + * @param string $condition + * + * @return $this + */ + final public function condition($condition) + { + $this->route->setCondition($condition); + + return $this; + } + + /** + * Sets the pattern for the host. + * + * @param string $pattern + * + * @return $this + */ + final public function host($pattern) + { + $this->route->setHost($pattern); + + return $this; + } + + /** + * Sets the schemes (e.g. 'https') this route is restricted to. + * So an empty array means that any scheme is allowed. + * + * @param string[] $schemes + * + * @return $this + */ + final public function schemes(array $schemes) + { + $this->route->setSchemes($schemes); + + return $this; + } + + /** + * Sets the HTTP methods (e.g. 'POST') this route is restricted to. + * So an empty array means that any method is allowed. + * + * @param string[] $methods + * + * @return $this + */ + final public function methods(array $methods) + { + $this->route->setMethods($methods); + + return $this; + } + + /** + * Adds the "_controller" entry to defaults. + * + * @param callable|string $controller a callable or parseable pseudo-callable + * + * @return $this + */ + final public function controller($controller) + { + $this->route->addDefaults(array('_controller' => $controller)); + + return $this; + } +} diff --git a/lib/silex/vendor/symfony/routing/Loader/DependencyInjection/ServiceRouterLoader.php b/lib/silex/vendor/symfony/routing/Loader/DependencyInjection/ServiceRouterLoader.php new file mode 100644 index 000000000..6c1621635 --- /dev/null +++ b/lib/silex/vendor/symfony/routing/Loader/DependencyInjection/ServiceRouterLoader.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\Routing\Loader\DependencyInjection; + +use Psr\Container\ContainerInterface; +use Symfony\Component\Routing\Loader\ObjectRouteLoader; + +/** + * A route loader that executes a service to load the routes. + * + * This depends on the DependencyInjection component. + * + * @author Ryan Weaver + */ +class ServiceRouterLoader extends ObjectRouteLoader +{ + /** + * @var ContainerInterface + */ + private $container; + + public function __construct(ContainerInterface $container) + { + $this->container = $container; + } + + protected function getServiceObject($id) + { + return $this->container->get($id); + } +} diff --git a/lib/silex/vendor/symfony/routing/Loader/DirectoryLoader.php b/lib/silex/vendor/symfony/routing/Loader/DirectoryLoader.php new file mode 100644 index 000000000..4bb5b31b6 --- /dev/null +++ b/lib/silex/vendor/symfony/routing/Loader/DirectoryLoader.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\Routing\Loader; + +use Symfony\Component\Config\Loader\FileLoader; +use Symfony\Component\Routing\RouteCollection; +use Symfony\Component\Config\Resource\DirectoryResource; + +class DirectoryLoader extends FileLoader +{ + /** + * {@inheritdoc} + */ + public function load($file, $type = null) + { + $path = $this->locator->locate($file); + + $collection = new RouteCollection(); + $collection->addResource(new DirectoryResource($path)); + + foreach (scandir($path) as $dir) { + if ('.' !== $dir[0]) { + $this->setCurrentDir($path); + $subPath = $path.'/'.$dir; + $subType = null; + + if (is_dir($subPath)) { + $subPath .= '/'; + $subType = 'directory'; + } + + $subCollection = $this->import($subPath, $subType, false, $path); + $collection->addCollection($subCollection); + } + } + + return $collection; + } + + /** + * {@inheritdoc} + */ + public function supports($resource, $type = null) + { + // only when type is forced to directory, not to conflict with AnnotationLoader + + return 'directory' === $type; + } +} diff --git a/lib/silex/vendor/symfony/routing/Loader/ObjectRouteLoader.php b/lib/silex/vendor/symfony/routing/Loader/ObjectRouteLoader.php new file mode 100644 index 000000000..0899a8181 --- /dev/null +++ b/lib/silex/vendor/symfony/routing/Loader/ObjectRouteLoader.php @@ -0,0 +1,95 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Routing\Loader; + +use Symfony\Component\Config\Loader\Loader; +use Symfony\Component\Config\Resource\FileResource; +use Symfony\Component\Routing\RouteCollection; + +/** + * A route loader that calls a method on an object to load the routes. + * + * @author Ryan Weaver + */ +abstract class ObjectRouteLoader extends Loader +{ + /** + * Returns the object that the method will be called on to load routes. + * + * For example, if your application uses a service container, + * the $id may be a service id. + * + * @param string $id + * + * @return object + */ + abstract protected function getServiceObject($id); + + /** + * Calls the service that will load the routes. + * + * @param mixed $resource Some value that will resolve to a callable + * @param string|null $type The resource type + * + * @return RouteCollection + */ + public function load($resource, $type = null) + { + $parts = explode(':', $resource); + if (2 != count($parts)) { + throw new \InvalidArgumentException(sprintf('Invalid resource "%s" passed to the "service" route loader: use the format "service_name:methodName"', $resource)); + } + + $serviceString = $parts[0]; + $method = $parts[1]; + + $loaderObject = $this->getServiceObject($serviceString); + + if (!is_object($loaderObject)) { + throw new \LogicException(sprintf('%s:getServiceObject() must return an object: %s returned', get_class($this), gettype($loaderObject))); + } + + if (!method_exists($loaderObject, $method)) { + throw new \BadMethodCallException(sprintf('Method "%s" not found on "%s" when importing routing resource "%s"', $method, get_class($loaderObject), $resource)); + } + + $routeCollection = call_user_func(array($loaderObject, $method), $this); + + if (!$routeCollection instanceof RouteCollection) { + $type = is_object($routeCollection) ? get_class($routeCollection) : gettype($routeCollection); + + throw new \LogicException(sprintf('The %s::%s method must return a RouteCollection: %s returned', get_class($loaderObject), $method, $type)); + } + + // make the service file tracked so that if it changes, the cache rebuilds + $this->addClassResource(new \ReflectionClass($loaderObject), $routeCollection); + + return $routeCollection; + } + + /** + * {@inheritdoc} + */ + public function supports($resource, $type = null) + { + return 'service' === $type; + } + + private function addClassResource(\ReflectionClass $class, RouteCollection $collection) + { + do { + if (is_file($class->getFileName())) { + $collection->addResource(new FileResource($class->getFileName())); + } + } while ($class = $class->getParentClass()); + } +} diff --git a/lib/silex/vendor/symfony/routing/Loader/PhpFileLoader.php b/lib/silex/vendor/symfony/routing/Loader/PhpFileLoader.php new file mode 100644 index 000000000..3fcd692f9 --- /dev/null +++ b/lib/silex/vendor/symfony/routing/Loader/PhpFileLoader.php @@ -0,0 +1,75 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Routing\Loader; + +use Symfony\Component\Config\Loader\FileLoader; +use Symfony\Component\Config\Resource\FileResource; +use Symfony\Component\Routing\Loader\Configurator\RoutingConfigurator; +use Symfony\Component\Routing\RouteCollection; + +/** + * PhpFileLoader loads routes from a PHP file. + * + * The file must return a RouteCollection instance. + * + * @author Fabien Potencier + */ +class PhpFileLoader extends FileLoader +{ + /** + * Loads a PHP file. + * + * @param string $file A PHP file path + * @param string|null $type The resource type + * + * @return RouteCollection A RouteCollection instance + */ + public function load($file, $type = null) + { + $path = $this->locator->locate($file); + $this->setCurrentDir(dirname($path)); + + // the closure forbids access to the private scope in the included file + $loader = $this; + $load = \Closure::bind(function ($file) use ($loader) { + return include $file; + }, null, ProtectedPhpFileLoader::class); + + $result = $load($path); + + if ($result instanceof \Closure) { + $collection = new RouteCollection(); + $result(new RoutingConfigurator($collection, $this, $path, $file), $this); + } else { + $collection = $result; + } + + $collection->addResource(new FileResource($path)); + + return $collection; + } + + /** + * {@inheritdoc} + */ + public function supports($resource, $type = null) + { + return is_string($resource) && 'php' === pathinfo($resource, PATHINFO_EXTENSION) && (!$type || 'php' === $type); + } +} + +/** + * @internal + */ +final class ProtectedPhpFileLoader extends PhpFileLoader +{ +} diff --git a/lib/silex/vendor/symfony/routing/Loader/XmlFileLoader.php b/lib/silex/vendor/symfony/routing/Loader/XmlFileLoader.php new file mode 100644 index 000000000..3a7789070 --- /dev/null +++ b/lib/silex/vendor/symfony/routing/Loader/XmlFileLoader.php @@ -0,0 +1,352 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Routing\Loader; + +use Symfony\Component\Routing\RouteCollection; +use Symfony\Component\Routing\Route; +use Symfony\Component\Config\Resource\FileResource; +use Symfony\Component\Config\Loader\FileLoader; +use Symfony\Component\Config\Util\XmlUtils; + +/** + * XmlFileLoader loads XML routing files. + * + * @author Fabien Potencier + * @author Tobias Schultze + */ +class XmlFileLoader extends FileLoader +{ + const NAMESPACE_URI = 'http://symfony.com/schema/routing'; + const SCHEME_PATH = '/schema/routing/routing-1.0.xsd'; + + /** + * Loads an XML file. + * + * @param string $file An XML file path + * @param string|null $type The resource type + * + * @return RouteCollection A RouteCollection instance + * + * @throws \InvalidArgumentException when the file cannot be loaded or when the XML cannot be + * parsed because it does not validate against the scheme + */ + public function load($file, $type = null) + { + $path = $this->locator->locate($file); + + $xml = $this->loadFile($path); + + $collection = new RouteCollection(); + $collection->addResource(new FileResource($path)); + + // process routes and imports + foreach ($xml->documentElement->childNodes as $node) { + if (!$node instanceof \DOMElement) { + continue; + } + + $this->parseNode($collection, $node, $path, $file); + } + + return $collection; + } + + /** + * Parses a node from a loaded XML file. + * + * @param RouteCollection $collection Collection to associate with the node + * @param \DOMElement $node Element to parse + * @param string $path Full path of the XML file being processed + * @param string $file Loaded file name + * + * @throws \InvalidArgumentException When the XML is invalid + */ + protected function parseNode(RouteCollection $collection, \DOMElement $node, $path, $file) + { + if (self::NAMESPACE_URI !== $node->namespaceURI) { + return; + } + + switch ($node->localName) { + case 'route': + $this->parseRoute($collection, $node, $path); + break; + case 'import': + $this->parseImport($collection, $node, $path, $file); + break; + default: + throw new \InvalidArgumentException(sprintf('Unknown tag "%s" used in file "%s". Expected "route" or "import".', $node->localName, $path)); + } + } + + /** + * {@inheritdoc} + */ + public function supports($resource, $type = null) + { + return is_string($resource) && 'xml' === pathinfo($resource, PATHINFO_EXTENSION) && (!$type || 'xml' === $type); + } + + /** + * Parses a route and adds it to the RouteCollection. + * + * @param RouteCollection $collection RouteCollection instance + * @param \DOMElement $node Element to parse that represents a Route + * @param string $path Full path of the XML file being processed + * + * @throws \InvalidArgumentException When the XML is invalid + */ + protected function parseRoute(RouteCollection $collection, \DOMElement $node, $path) + { + if ('' === ($id = $node->getAttribute('id')) || !$node->hasAttribute('path')) { + throw new \InvalidArgumentException(sprintf('The element in file "%s" must have an "id" and a "path" attribute.', $path)); + } + + $schemes = preg_split('/[\s,\|]++/', $node->getAttribute('schemes'), -1, PREG_SPLIT_NO_EMPTY); + $methods = preg_split('/[\s,\|]++/', $node->getAttribute('methods'), -1, PREG_SPLIT_NO_EMPTY); + + list($defaults, $requirements, $options, $condition) = $this->parseConfigs($node, $path); + + $route = new Route($node->getAttribute('path'), $defaults, $requirements, $options, $node->getAttribute('host'), $schemes, $methods, $condition); + $collection->add($id, $route); + } + + /** + * Parses an import and adds the routes in the resource to the RouteCollection. + * + * @param RouteCollection $collection RouteCollection instance + * @param \DOMElement $node Element to parse that represents a Route + * @param string $path Full path of the XML file being processed + * @param string $file Loaded file name + * + * @throws \InvalidArgumentException When the XML is invalid + */ + protected function parseImport(RouteCollection $collection, \DOMElement $node, $path, $file) + { + if ('' === $resource = $node->getAttribute('resource')) { + throw new \InvalidArgumentException(sprintf('The element in file "%s" must have a "resource" attribute.', $path)); + } + + $type = $node->getAttribute('type'); + $prefix = $node->getAttribute('prefix'); + $host = $node->hasAttribute('host') ? $node->getAttribute('host') : null; + $schemes = $node->hasAttribute('schemes') ? preg_split('/[\s,\|]++/', $node->getAttribute('schemes'), -1, PREG_SPLIT_NO_EMPTY) : null; + $methods = $node->hasAttribute('methods') ? preg_split('/[\s,\|]++/', $node->getAttribute('methods'), -1, PREG_SPLIT_NO_EMPTY) : null; + + list($defaults, $requirements, $options, $condition) = $this->parseConfigs($node, $path); + + $this->setCurrentDir(dirname($path)); + + $subCollection = $this->import($resource, ('' !== $type ? $type : null), false, $file); + /* @var $subCollection RouteCollection */ + $subCollection->addPrefix($prefix); + if (null !== $host) { + $subCollection->setHost($host); + } + if (null !== $condition) { + $subCollection->setCondition($condition); + } + if (null !== $schemes) { + $subCollection->setSchemes($schemes); + } + if (null !== $methods) { + $subCollection->setMethods($methods); + } + $subCollection->addDefaults($defaults); + $subCollection->addRequirements($requirements); + $subCollection->addOptions($options); + + $collection->addCollection($subCollection); + } + + /** + * Loads an XML file. + * + * @param string $file An XML file path + * + * @return \DOMDocument + * + * @throws \InvalidArgumentException When loading of XML file fails because of syntax errors + * or when the XML structure is not as expected by the scheme - + * see validate() + */ + protected function loadFile($file) + { + return XmlUtils::loadFile($file, __DIR__.static::SCHEME_PATH); + } + + /** + * Parses the config elements (default, requirement, option). + * + * @param \DOMElement $node Element to parse that contains the configs + * @param string $path Full path of the XML file being processed + * + * @return array An array with the defaults as first item, requirements as second and options as third + * + * @throws \InvalidArgumentException When the XML is invalid + */ + private function parseConfigs(\DOMElement $node, $path) + { + $defaults = array(); + $requirements = array(); + $options = array(); + $condition = null; + + foreach ($node->getElementsByTagNameNS(self::NAMESPACE_URI, '*') as $n) { + if ($node !== $n->parentNode) { + continue; + } + + switch ($n->localName) { + case 'default': + if ($this->isElementValueNull($n)) { + $defaults[$n->getAttribute('key')] = null; + } else { + $defaults[$n->getAttribute('key')] = $this->parseDefaultsConfig($n, $path); + } + + break; + case 'requirement': + $requirements[$n->getAttribute('key')] = trim($n->textContent); + break; + case 'option': + $options[$n->getAttribute('key')] = trim($n->textContent); + break; + case 'condition': + $condition = trim($n->textContent); + break; + default: + throw new \InvalidArgumentException(sprintf('Unknown tag "%s" used in file "%s". Expected "default", "requirement", "option" or "condition".', $n->localName, $path)); + } + } + + if ($controller = $node->getAttribute('controller')) { + if (isset($defaults['_controller'])) { + $name = $node->hasAttribute('id') ? sprintf('"%s"', $node->getAttribute('id')) : sprintf('the "%s" tag', $node->tagName); + + throw new \InvalidArgumentException(sprintf('The routing file "%s" must not specify both the "controller" attribute and the defaults key "_controller" for %s.', $path, $name)); + } + + $defaults['_controller'] = $controller; + } + + return array($defaults, $requirements, $options, $condition); + } + + /** + * Parses the "default" elements. + * + * @param \DOMElement $element The "default" element to parse + * @param string $path Full path of the XML file being processed + * + * @return array|bool|float|int|string|null The parsed value of the "default" element + */ + private function parseDefaultsConfig(\DOMElement $element, $path) + { + if ($this->isElementValueNull($element)) { + return; + } + + // Check for existing element nodes in the default element. There can + // only be a single element inside a default element. So this element + // (if one was found) can safely be returned. + foreach ($element->childNodes as $child) { + if (!$child instanceof \DOMElement) { + continue; + } + + if (self::NAMESPACE_URI !== $child->namespaceURI) { + continue; + } + + return $this->parseDefaultNode($child, $path); + } + + // If the default element doesn't contain a nested "bool", "int", "float", + // "string", "list", or "map" element, the element contents will be treated + // as the string value of the associated default option. + return trim($element->textContent); + } + + /** + * Recursively parses the value of a "default" element. + * + * @param \DOMElement $node The node value + * @param string $path Full path of the XML file being processed + * + * @return array|bool|float|int|string The parsed value + * + * @throws \InvalidArgumentException when the XML is invalid + */ + private function parseDefaultNode(\DOMElement $node, $path) + { + if ($this->isElementValueNull($node)) { + return; + } + + switch ($node->localName) { + case 'bool': + return 'true' === trim($node->nodeValue) || '1' === trim($node->nodeValue); + case 'int': + return (int) trim($node->nodeValue); + case 'float': + return (float) trim($node->nodeValue); + case 'string': + return trim($node->nodeValue); + case 'list': + $list = array(); + + foreach ($node->childNodes as $element) { + if (!$element instanceof \DOMElement) { + continue; + } + + if (self::NAMESPACE_URI !== $element->namespaceURI) { + continue; + } + + $list[] = $this->parseDefaultNode($element, $path); + } + + return $list; + case 'map': + $map = array(); + + foreach ($node->childNodes as $element) { + if (!$element instanceof \DOMElement) { + continue; + } + + if (self::NAMESPACE_URI !== $element->namespaceURI) { + continue; + } + + $map[$element->getAttribute('key')] = $this->parseDefaultNode($element, $path); + } + + return $map; + default: + throw new \InvalidArgumentException(sprintf('Unknown tag "%s" used in file "%s". Expected "bool", "int", "float", "string", "list", or "map".', $node->localName, $path)); + } + } + + private function isElementValueNull(\DOMElement $element) + { + $namespaceUri = 'http://www.w3.org/2001/XMLSchema-instance'; + + if (!$element->hasAttributeNS($namespaceUri, 'nil')) { + return false; + } + + return 'true' === $element->getAttributeNS($namespaceUri, 'nil') || '1' === $element->getAttributeNS($namespaceUri, 'nil'); + } +} diff --git a/lib/silex/vendor/symfony/routing/Loader/YamlFileLoader.php b/lib/silex/vendor/symfony/routing/Loader/YamlFileLoader.php new file mode 100644 index 000000000..037d581d0 --- /dev/null +++ b/lib/silex/vendor/symfony/routing/Loader/YamlFileLoader.php @@ -0,0 +1,226 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Routing\Loader; + +use Symfony\Component\Routing\RouteCollection; +use Symfony\Component\Routing\Route; +use Symfony\Component\Config\Resource\FileResource; +use Symfony\Component\Yaml\Exception\ParseException; +use Symfony\Component\Yaml\Parser as YamlParser; +use Symfony\Component\Config\Loader\FileLoader; + +/** + * YamlFileLoader loads Yaml routing files. + * + * @author Fabien Potencier + * @author Tobias Schultze + */ +class YamlFileLoader extends FileLoader +{ + private static $availableKeys = array( + 'resource', 'type', 'prefix', 'path', 'host', 'schemes', 'methods', 'defaults', 'requirements', 'options', 'condition', 'controller', + ); + private $yamlParser; + + /** + * Loads a Yaml file. + * + * @param string $file A Yaml file path + * @param string|null $type The resource type + * + * @return RouteCollection A RouteCollection instance + * + * @throws \InvalidArgumentException When a route can't be parsed because YAML is invalid + */ + public function load($file, $type = null) + { + $path = $this->locator->locate($file); + + if (!stream_is_local($path)) { + throw new \InvalidArgumentException(sprintf('This is not a local file "%s".', $path)); + } + + if (!file_exists($path)) { + throw new \InvalidArgumentException(sprintf('File "%s" not found.', $path)); + } + + if (null === $this->yamlParser) { + $this->yamlParser = new YamlParser(); + } + + $prevErrorHandler = set_error_handler(function ($level, $message, $script, $line) use ($file, &$prevErrorHandler) { + $message = E_USER_DEPRECATED === $level ? preg_replace('/ on line \d+/', ' in "'.$file.'"$0', $message) : $message; + + return $prevErrorHandler ? $prevErrorHandler($level, $message, $script, $line) : false; + }); + + try { + $parsedConfig = $this->yamlParser->parseFile($path); + } catch (ParseException $e) { + throw new \InvalidArgumentException(sprintf('The file "%s" does not contain valid YAML.', $path), 0, $e); + } finally { + restore_error_handler(); + } + + $collection = new RouteCollection(); + $collection->addResource(new FileResource($path)); + + // empty file + if (null === $parsedConfig) { + return $collection; + } + + // not an array + if (!is_array($parsedConfig)) { + throw new \InvalidArgumentException(sprintf('The file "%s" must contain a YAML array.', $path)); + } + + foreach ($parsedConfig as $name => $config) { + $this->validate($config, $name, $path); + + if (isset($config['resource'])) { + $this->parseImport($collection, $config, $path, $file); + } else { + $this->parseRoute($collection, $name, $config, $path); + } + } + + return $collection; + } + + /** + * {@inheritdoc} + */ + public function supports($resource, $type = null) + { + return is_string($resource) && in_array(pathinfo($resource, PATHINFO_EXTENSION), array('yml', 'yaml'), true) && (!$type || 'yaml' === $type); + } + + /** + * Parses a route and adds it to the RouteCollection. + * + * @param RouteCollection $collection A RouteCollection instance + * @param string $name Route name + * @param array $config Route definition + * @param string $path Full path of the YAML file being processed + */ + protected function parseRoute(RouteCollection $collection, $name, array $config, $path) + { + $defaults = isset($config['defaults']) ? $config['defaults'] : array(); + $requirements = isset($config['requirements']) ? $config['requirements'] : array(); + $options = isset($config['options']) ? $config['options'] : array(); + $host = isset($config['host']) ? $config['host'] : ''; + $schemes = isset($config['schemes']) ? $config['schemes'] : array(); + $methods = isset($config['methods']) ? $config['methods'] : array(); + $condition = isset($config['condition']) ? $config['condition'] : null; + + if (isset($config['controller'])) { + $defaults['_controller'] = $config['controller']; + } + + $route = new Route($config['path'], $defaults, $requirements, $options, $host, $schemes, $methods, $condition); + + $collection->add($name, $route); + } + + /** + * Parses an import and adds the routes in the resource to the RouteCollection. + * + * @param RouteCollection $collection A RouteCollection instance + * @param array $config Route definition + * @param string $path Full path of the YAML file being processed + * @param string $file Loaded file name + */ + protected function parseImport(RouteCollection $collection, array $config, $path, $file) + { + $type = isset($config['type']) ? $config['type'] : null; + $prefix = isset($config['prefix']) ? $config['prefix'] : ''; + $defaults = isset($config['defaults']) ? $config['defaults'] : array(); + $requirements = isset($config['requirements']) ? $config['requirements'] : array(); + $options = isset($config['options']) ? $config['options'] : array(); + $host = isset($config['host']) ? $config['host'] : null; + $condition = isset($config['condition']) ? $config['condition'] : null; + $schemes = isset($config['schemes']) ? $config['schemes'] : null; + $methods = isset($config['methods']) ? $config['methods'] : null; + + if (isset($config['controller'])) { + $defaults['_controller'] = $config['controller']; + } + + $this->setCurrentDir(dirname($path)); + + $subCollection = $this->import($config['resource'], $type, false, $file); + /* @var $subCollection RouteCollection */ + $subCollection->addPrefix($prefix); + if (null !== $host) { + $subCollection->setHost($host); + } + if (null !== $condition) { + $subCollection->setCondition($condition); + } + if (null !== $schemes) { + $subCollection->setSchemes($schemes); + } + if (null !== $methods) { + $subCollection->setMethods($methods); + } + $subCollection->addDefaults($defaults); + $subCollection->addRequirements($requirements); + $subCollection->addOptions($options); + + $collection->addCollection($subCollection); + } + + /** + * Validates the route configuration. + * + * @param array $config A resource config + * @param string $name The config key + * @param string $path The loaded file path + * + * @throws \InvalidArgumentException If one of the provided config keys is not supported, + * something is missing or the combination is nonsense + */ + protected function validate($config, $name, $path) + { + if (!is_array($config)) { + throw new \InvalidArgumentException(sprintf('The definition of "%s" in "%s" must be a YAML array.', $name, $path)); + } + if ($extraKeys = array_diff(array_keys($config), self::$availableKeys)) { + throw new \InvalidArgumentException(sprintf( + 'The routing file "%s" contains unsupported keys for "%s": "%s". Expected one of: "%s".', + $path, $name, implode('", "', $extraKeys), implode('", "', self::$availableKeys) + )); + } + if (isset($config['resource']) && isset($config['path'])) { + throw new \InvalidArgumentException(sprintf( + 'The routing file "%s" must not specify both the "resource" key and the "path" key for "%s". Choose between an import and a route definition.', + $path, $name + )); + } + if (!isset($config['resource']) && isset($config['type'])) { + throw new \InvalidArgumentException(sprintf( + 'The "type" key for the route definition "%s" in "%s" is unsupported. It is only available for imports in combination with the "resource" key.', + $name, $path + )); + } + if (!isset($config['resource']) && !isset($config['path'])) { + throw new \InvalidArgumentException(sprintf( + 'You must define a "path" for the route "%s" in file "%s".', + $name, $path + )); + } + if (isset($config['controller']) && isset($config['defaults']['_controller'])) { + throw new \InvalidArgumentException(sprintf('The routing file "%s" must not specify both the "controller" key and the defaults key "_controller" for "%s".', $path, $name)); + } + } +} diff --git a/lib/silex/vendor/symfony/routing/Loader/schema/routing/routing-1.0.xsd b/lib/silex/vendor/symfony/routing/Loader/schema/routing/routing-1.0.xsd new file mode 100644 index 000000000..a97111aaa --- /dev/null +++ b/lib/silex/vendor/symfony/routing/Loader/schema/routing/routing-1.0.xsd @@ -0,0 +1,148 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/silex/vendor/symfony/routing/Matcher/Dumper/DumperCollection.php b/lib/silex/vendor/symfony/routing/Matcher/Dumper/DumperCollection.php new file mode 100644 index 000000000..6916297b8 --- /dev/null +++ b/lib/silex/vendor/symfony/routing/Matcher/Dumper/DumperCollection.php @@ -0,0 +1,159 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Routing\Matcher\Dumper; + +/** + * Collection of routes. + * + * @author Arnaud Le Blanc + * + * @internal + */ +class DumperCollection implements \IteratorAggregate +{ + /** + * @var DumperCollection|null + */ + private $parent; + + /** + * @var DumperCollection[]|DumperRoute[] + */ + private $children = array(); + + /** + * @var array + */ + private $attributes = array(); + + /** + * Returns the children routes and collections. + * + * @return self[]|DumperRoute[] + */ + public function all() + { + return $this->children; + } + + /** + * Adds a route or collection. + * + * @param DumperRoute|DumperCollection The route or collection + */ + public function add($child) + { + if ($child instanceof self) { + $child->setParent($this); + } + $this->children[] = $child; + } + + /** + * Sets children. + * + * @param array $children The children + */ + public function setAll(array $children) + { + foreach ($children as $child) { + if ($child instanceof self) { + $child->setParent($this); + } + } + $this->children = $children; + } + + /** + * Returns an iterator over the children. + * + * @return \Iterator|DumperCollection[]|DumperRoute[] The iterator + */ + public function getIterator() + { + return new \ArrayIterator($this->children); + } + + /** + * Returns the root of the collection. + * + * @return self The root collection + */ + public function getRoot() + { + return (null !== $this->parent) ? $this->parent->getRoot() : $this; + } + + /** + * Returns the parent collection. + * + * @return self|null The parent collection or null if the collection has no parent + */ + protected function getParent() + { + return $this->parent; + } + + /** + * Sets the parent collection. + */ + protected function setParent(DumperCollection $parent) + { + $this->parent = $parent; + } + + /** + * Returns true if the attribute is defined. + * + * @param string $name The attribute name + * + * @return bool true if the attribute is defined, false otherwise + */ + public function hasAttribute($name) + { + return array_key_exists($name, $this->attributes); + } + + /** + * Returns an attribute by name. + * + * @param string $name The attribute name + * @param mixed $default Default value is the attribute doesn't exist + * + * @return mixed The attribute value + */ + public function getAttribute($name, $default = null) + { + return $this->hasAttribute($name) ? $this->attributes[$name] : $default; + } + + /** + * Sets an attribute by name. + * + * @param string $name The attribute name + * @param mixed $value The attribute value + */ + public function setAttribute($name, $value) + { + $this->attributes[$name] = $value; + } + + /** + * Sets multiple attributes. + * + * @param array $attributes The attributes + */ + public function setAttributes($attributes) + { + $this->attributes = $attributes; + } +} diff --git a/lib/silex/vendor/symfony/routing/Matcher/Dumper/DumperRoute.php b/lib/silex/vendor/symfony/routing/Matcher/Dumper/DumperRoute.php new file mode 100644 index 000000000..c71989a3a --- /dev/null +++ b/lib/silex/vendor/symfony/routing/Matcher/Dumper/DumperRoute.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\Routing\Matcher\Dumper; + +use Symfony\Component\Routing\Route; + +/** + * Container for a Route. + * + * @author Arnaud Le Blanc + * + * @internal + */ +class DumperRoute +{ + private $name; + private $route; + + /** + * @param string $name The route name + * @param Route $route The route + */ + public function __construct($name, Route $route) + { + $this->name = $name; + $this->route = $route; + } + + /** + * Returns the route name. + * + * @return string The route name + */ + public function getName() + { + return $this->name; + } + + /** + * Returns the route. + * + * @return Route The route + */ + public function getRoute() + { + return $this->route; + } +} diff --git a/lib/silex/vendor/symfony/routing/Matcher/Dumper/MatcherDumper.php b/lib/silex/vendor/symfony/routing/Matcher/Dumper/MatcherDumper.php new file mode 100644 index 000000000..ea51ab406 --- /dev/null +++ b/lib/silex/vendor/symfony/routing/Matcher/Dumper/MatcherDumper.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\Routing\Matcher\Dumper; + +use Symfony\Component\Routing\RouteCollection; + +/** + * MatcherDumper is the abstract class for all built-in matcher dumpers. + * + * @author Fabien Potencier + */ +abstract class MatcherDumper implements MatcherDumperInterface +{ + private $routes; + + public function __construct(RouteCollection $routes) + { + $this->routes = $routes; + } + + /** + * {@inheritdoc} + */ + public function getRoutes() + { + return $this->routes; + } +} diff --git a/lib/silex/vendor/symfony/routing/Matcher/Dumper/MatcherDumperInterface.php b/lib/silex/vendor/symfony/routing/Matcher/Dumper/MatcherDumperInterface.php new file mode 100644 index 000000000..5e7c134b9 --- /dev/null +++ b/lib/silex/vendor/symfony/routing/Matcher/Dumper/MatcherDumperInterface.php @@ -0,0 +1,39 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Routing\Matcher\Dumper; + +use Symfony\Component\Routing\RouteCollection; + +/** + * MatcherDumperInterface is the interface that all matcher dumper classes must implement. + * + * @author Fabien Potencier + */ +interface MatcherDumperInterface +{ + /** + * Dumps a set of routes to a string representation of executable code + * that can then be used to match a request against these routes. + * + * @param array $options An array of options + * + * @return string Executable code + */ + public function dump(array $options = array()); + + /** + * Gets the routes to dump. + * + * @return RouteCollection A RouteCollection instance + */ + public function getRoutes(); +} diff --git a/lib/silex/vendor/symfony/routing/Matcher/Dumper/PhpMatcherDumper.php b/lib/silex/vendor/symfony/routing/Matcher/Dumper/PhpMatcherDumper.php new file mode 100644 index 000000000..acb9eddb3 --- /dev/null +++ b/lib/silex/vendor/symfony/routing/Matcher/Dumper/PhpMatcherDumper.php @@ -0,0 +1,441 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Routing\Matcher\Dumper; + +use Symfony\Component\Routing\Route; +use Symfony\Component\Routing\RouteCollection; +use Symfony\Component\ExpressionLanguage\ExpressionLanguage; +use Symfony\Component\ExpressionLanguage\ExpressionFunctionProviderInterface; + +/** + * PhpMatcherDumper creates a PHP class able to match URLs for a given set of routes. + * + * @author Fabien Potencier + * @author Tobias Schultze + * @author Arnaud Le Blanc + */ +class PhpMatcherDumper extends MatcherDumper +{ + private $expressionLanguage; + + /** + * @var ExpressionFunctionProviderInterface[] + */ + private $expressionLanguageProviders = array(); + + /** + * Dumps a set of routes to a PHP class. + * + * Available options: + * + * * class: The class name + * * base_class: The base class name + * + * @param array $options An array of options + * + * @return string A PHP class representing the matcher class + */ + public function dump(array $options = array()) + { + $options = array_replace(array( + 'class' => 'ProjectUrlMatcher', + 'base_class' => 'Symfony\\Component\\Routing\\Matcher\\UrlMatcher', + ), $options); + + // trailing slash support is only enabled if we know how to redirect the user + $interfaces = class_implements($options['base_class']); + $supportsRedirections = isset($interfaces['Symfony\\Component\\Routing\\Matcher\\RedirectableUrlMatcherInterface']); + + return <<context = \$context; + } + +{$this->generateMatchMethod($supportsRedirections)} +} + +EOF; + } + + public function addExpressionLanguageProvider(ExpressionFunctionProviderInterface $provider) + { + $this->expressionLanguageProviders[] = $provider; + } + + /** + * Generates the code for the match method implementing UrlMatcherInterface. + * + * @param bool $supportsRedirections Whether redirections are supported by the base class + * + * @return string Match method as PHP code + */ + private function generateMatchMethod($supportsRedirections) + { + $code = rtrim($this->compileRoutes($this->getRoutes(), $supportsRedirections), "\n"); + + return <<context; + \$request = \$this->request; + \$requestMethod = \$canonicalMethod = \$context->getMethod(); + \$scheme = \$context->getScheme(); + + if ('HEAD' === \$requestMethod) { + \$canonicalMethod = 'GET'; + } + + +$code + + throw 0 < count(\$allow) ? new MethodNotAllowedException(array_unique(\$allow)) : new ResourceNotFoundException(); + } +EOF; + } + + /** + * Generates PHP code to match a RouteCollection with all its routes. + * + * @param RouteCollection $routes A RouteCollection instance + * @param bool $supportsRedirections Whether redirections are supported by the base class + * + * @return string PHP code + */ + private function compileRoutes(RouteCollection $routes, $supportsRedirections) + { + $fetchedHost = false; + $groups = $this->groupRoutesByHostRegex($routes); + $code = ''; + + foreach ($groups as $collection) { + if (null !== $regex = $collection->getAttribute('host_regex')) { + if (!$fetchedHost) { + $code .= " \$host = \$context->getHost();\n\n"; + $fetchedHost = true; + } + + $code .= sprintf(" if (preg_match(%s, \$host, \$hostMatches)) {\n", var_export($regex, true)); + } + + $tree = $this->buildStaticPrefixCollection($collection); + $groupCode = $this->compileStaticPrefixRoutes($tree, $supportsRedirections); + + if (null !== $regex) { + // apply extra indention at each line (except empty ones) + $groupCode = preg_replace('/^.{2,}$/m', ' $0', $groupCode); + $code .= $groupCode; + $code .= " }\n\n"; + } else { + $code .= $groupCode; + } + } + + if ('' === $code) { + $code .= " if ('/' === \$pathinfo) {\n"; + $code .= " throw new Symfony\Component\Routing\Exception\NoConfigurationException();\n"; + $code .= " }\n"; + } + + return $code; + } + + private function buildStaticPrefixCollection(DumperCollection $collection) + { + $prefixCollection = new StaticPrefixCollection(); + + foreach ($collection as $dumperRoute) { + $prefix = $dumperRoute->getRoute()->compile()->getStaticPrefix(); + $prefixCollection->addRoute($prefix, $dumperRoute); + } + + $prefixCollection->optimizeGroups(); + + return $prefixCollection; + } + + /** + * Generates PHP code to match a tree of routes. + * + * @param StaticPrefixCollection $collection A StaticPrefixCollection instance + * @param bool $supportsRedirections Whether redirections are supported by the base class + * @param string $ifOrElseIf either "if" or "elseif" to influence chaining + * + * @return string PHP code + */ + private function compileStaticPrefixRoutes(StaticPrefixCollection $collection, $supportsRedirections, $ifOrElseIf = 'if') + { + $code = ''; + $prefix = $collection->getPrefix(); + + if (!empty($prefix) && '/' !== $prefix) { + $code .= sprintf(" %s (0 === strpos(\$pathinfo, %s)) {\n", $ifOrElseIf, var_export($prefix, true)); + } + + $ifOrElseIf = 'if'; + + foreach ($collection->getItems() as $route) { + if ($route instanceof StaticPrefixCollection) { + $code .= $this->compileStaticPrefixRoutes($route, $supportsRedirections, $ifOrElseIf); + $ifOrElseIf = 'elseif'; + } else { + $code .= $this->compileRoute($route[1]->getRoute(), $route[1]->getName(), $supportsRedirections, $prefix)."\n"; + $ifOrElseIf = 'if'; + } + } + + if (!empty($prefix) && '/' !== $prefix) { + $code .= " }\n\n"; + // apply extra indention at each line (except empty ones) + $code = preg_replace('/^.{2,}$/m', ' $0', $code); + } + + return $code; + } + + /** + * Compiles a single Route to PHP code used to match it against the path info. + * + * @param Route $route A Route instance + * @param string $name The name of the Route + * @param bool $supportsRedirections Whether redirections are supported by the base class + * @param string|null $parentPrefix The prefix of the parent collection used to optimize the code + * + * @return string PHP code + * + * @throws \LogicException + */ + private function compileRoute(Route $route, $name, $supportsRedirections, $parentPrefix = null) + { + $code = ''; + $compiledRoute = $route->compile(); + $conditions = array(); + $hasTrailingSlash = false; + $matches = false; + $hostMatches = false; + $methods = $route->getMethods(); + + $supportsTrailingSlash = $supportsRedirections && (!$methods || in_array('HEAD', $methods) || in_array('GET', $methods)); + $regex = $compiledRoute->getRegex(); + + if (!count($compiledRoute->getPathVariables()) && false !== preg_match('#^(.)\^(?P.*?)\$\1#'.('u' === substr($regex, -1) ? 'u' : ''), $regex, $m)) { + if ($supportsTrailingSlash && '/' === substr($m['url'], -1)) { + $conditions[] = sprintf('%s === $trimmedPathinfo', var_export(rtrim(str_replace('\\', '', $m['url']), '/'), true)); + $hasTrailingSlash = true; + } else { + $conditions[] = sprintf('%s === $pathinfo', var_export(str_replace('\\', '', $m['url']), true)); + } + } else { + if ($compiledRoute->getStaticPrefix() && $compiledRoute->getStaticPrefix() !== $parentPrefix) { + $conditions[] = sprintf('0 === strpos($pathinfo, %s)', var_export($compiledRoute->getStaticPrefix(), true)); + } + + if ($supportsTrailingSlash && $pos = strpos($regex, '/$')) { + $regex = substr($regex, 0, $pos).'/?$'.substr($regex, $pos + 2); + $hasTrailingSlash = true; + } + $conditions[] = sprintf('preg_match(%s, $pathinfo, $matches)', var_export($regex, true)); + + $matches = true; + } + + if ($compiledRoute->getHostVariables()) { + $hostMatches = true; + } + + if ($route->getCondition()) { + $conditions[] = $this->getExpressionLanguage()->compile($route->getCondition(), array('context', 'request')); + } + + $conditions = implode(' && ', $conditions); + + $code .= <<mergeDefaults(array_replace(%s), %s);\n", + implode(', ', $vars), + str_replace("\n", '', var_export($route->getDefaults(), true)) + ); + } elseif ($route->getDefaults()) { + $code .= sprintf(" \$ret = %s;\n", str_replace("\n", '', var_export(array_replace($route->getDefaults(), array('_route' => $name)), true))); + } else { + $code .= sprintf(" \$ret = array('_route' => '%s');\n", $name); + } + + if ($hasTrailingSlash) { + $code .= <<redirect(\$rawPathinfo.'/', '$name')); + } + + +EOF; + } + + if ($schemes = $route->getSchemes()) { + if (!$supportsRedirections) { + throw new \LogicException('The "schemes" requirement is only supported for URL matchers that implement RedirectableUrlMatcherInterface.'); + } + $schemes = str_replace("\n", '', var_export(array_flip($schemes), true)); + $code .= <<redirect(\$rawPathinfo, '$name', key(\$requiredSchemes))); + } + + +EOF; + } + + if ($hasTrailingSlash || $schemes) { + $code .= " return \$ret;\n"; + } else { + $code = substr_replace($code, 'return', $retOffset, 6); + } + $code .= " }\n"; + + if ($methods) { + $code .= " $gotoname:\n"; + } + + return $code; + } + + /** + * Groups consecutive routes having the same host regex. + * + * The result is a collection of collections of routes having the same host regex. + * + * @param RouteCollection $routes A flat RouteCollection + * + * @return DumperCollection A collection with routes grouped by host regex in sub-collections + */ + private function groupRoutesByHostRegex(RouteCollection $routes) + { + $groups = new DumperCollection(); + $currentGroup = new DumperCollection(); + $currentGroup->setAttribute('host_regex', null); + $groups->add($currentGroup); + + foreach ($routes as $name => $route) { + $hostRegex = $route->compile()->getHostRegex(); + if ($currentGroup->getAttribute('host_regex') !== $hostRegex) { + $currentGroup = new DumperCollection(); + $currentGroup->setAttribute('host_regex', $hostRegex); + $groups->add($currentGroup); + } + $currentGroup->add(new DumperRoute($name, $route)); + } + + return $groups; + } + + private function getExpressionLanguage() + { + if (null === $this->expressionLanguage) { + if (!class_exists('Symfony\Component\ExpressionLanguage\ExpressionLanguage')) { + throw new \RuntimeException('Unable to use expressions as the Symfony ExpressionLanguage component is not installed.'); + } + $this->expressionLanguage = new ExpressionLanguage(null, $this->expressionLanguageProviders); + } + + return $this->expressionLanguage; + } +} diff --git a/lib/silex/vendor/symfony/routing/Matcher/Dumper/StaticPrefixCollection.php b/lib/silex/vendor/symfony/routing/Matcher/Dumper/StaticPrefixCollection.php new file mode 100644 index 000000000..736580861 --- /dev/null +++ b/lib/silex/vendor/symfony/routing/Matcher/Dumper/StaticPrefixCollection.php @@ -0,0 +1,238 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Routing\Matcher\Dumper; + +/** + * Prefix tree of routes preserving routes order. + * + * @author Frank de Jonge + * + * @internal + */ +class StaticPrefixCollection +{ + /** + * @var string + */ + private $prefix; + + /** + * @var array[]|StaticPrefixCollection[] + */ + private $items = array(); + + /** + * @var int + */ + private $matchStart = 0; + + public function __construct($prefix = '') + { + $this->prefix = $prefix; + } + + public function getPrefix() + { + return $this->prefix; + } + + /** + * @return mixed[]|StaticPrefixCollection[] + */ + public function getItems() + { + return $this->items; + } + + /** + * Adds a route to a group. + * + * @param string $prefix + * @param mixed $route + */ + public function addRoute($prefix, $route) + { + $prefix = '/' === $prefix ? $prefix : rtrim($prefix, '/'); + $this->guardAgainstAddingNotAcceptedRoutes($prefix); + + if ($this->prefix === $prefix) { + // When a prefix is exactly the same as the base we move up the match start position. + // This is needed because otherwise routes that come afterwards have higher precedence + // than a possible regular expression, which goes against the input order sorting. + $this->items[] = array($prefix, $route); + $this->matchStart = count($this->items); + + return; + } + + foreach ($this->items as $i => $item) { + if ($i < $this->matchStart) { + continue; + } + + if ($item instanceof self && $item->accepts($prefix)) { + $item->addRoute($prefix, $route); + + return; + } + + $group = $this->groupWithItem($item, $prefix, $route); + + if ($group instanceof self) { + $this->items[$i] = $group; + + return; + } + } + + // No optimised case was found, in this case we simple add the route for possible + // grouping when new routes are added. + $this->items[] = array($prefix, $route); + } + + /** + * Tries to combine a route with another route or group. + * + * @param StaticPrefixCollection|array $item + * @param string $prefix + * @param mixed $route + * + * @return null|StaticPrefixCollection + */ + private function groupWithItem($item, $prefix, $route) + { + $itemPrefix = $item instanceof self ? $item->prefix : $item[0]; + $commonPrefix = $this->detectCommonPrefix($prefix, $itemPrefix); + + if (!$commonPrefix) { + return; + } + + $child = new self($commonPrefix); + + if ($item instanceof self) { + $child->items = array($item); + } else { + $child->addRoute($item[0], $item[1]); + } + + $child->addRoute($prefix, $route); + + return $child; + } + + /** + * Checks whether a prefix can be contained within the group. + * + * @param string $prefix + * + * @return bool Whether a prefix could belong in a given group + */ + private function accepts($prefix) + { + return '' === $this->prefix || 0 === strpos($prefix, $this->prefix); + } + + /** + * Detects whether there's a common prefix relative to the group prefix and returns it. + * + * @param string $prefix + * @param string $anotherPrefix + * + * @return false|string A common prefix, longer than the base/group prefix, or false when none available + */ + private function detectCommonPrefix($prefix, $anotherPrefix) + { + $baseLength = strlen($this->prefix); + $commonLength = $baseLength; + $end = min(strlen($prefix), strlen($anotherPrefix)); + + for ($i = $baseLength; $i <= $end; ++$i) { + if (substr($prefix, 0, $i) !== substr($anotherPrefix, 0, $i)) { + break; + } + + $commonLength = $i; + } + + $commonPrefix = rtrim(substr($prefix, 0, $commonLength), '/'); + + if (strlen($commonPrefix) > $baseLength) { + return $commonPrefix; + } + + return false; + } + + /** + * Optimizes the tree by inlining items from groups with less than 3 items. + */ + public function optimizeGroups() + { + $index = -1; + + while (isset($this->items[++$index])) { + $item = $this->items[$index]; + + if ($item instanceof self) { + $item->optimizeGroups(); + + // When a group contains only two items there's no reason to optimize because at minimum + // the amount of prefix check is 2. In this case inline the group. + if ($item->shouldBeInlined()) { + array_splice($this->items, $index, 1, $item->items); + + // Lower index to pass through the same index again after optimizing. + // The first item of the replacements might be a group needing optimization. + --$index; + } + } + } + } + + private function shouldBeInlined() + { + if (count($this->items) >= 3) { + return false; + } + + foreach ($this->items as $item) { + if ($item instanceof self) { + return true; + } + } + + foreach ($this->items as $item) { + if (is_array($item) && $item[0] === $this->prefix) { + return false; + } + } + + return true; + } + + /** + * Guards against adding incompatible prefixes in a group. + * + * @param string $prefix + * + * @throws \LogicException when a prefix does not belong in a group + */ + private function guardAgainstAddingNotAcceptedRoutes($prefix) + { + if (!$this->accepts($prefix)) { + $message = sprintf('Could not add route with prefix %s to collection with prefix %s', $prefix, $this->prefix); + + throw new \LogicException($message); + } + } +} diff --git a/lib/silex/vendor/symfony/routing/Matcher/RedirectableUrlMatcher.php b/lib/silex/vendor/symfony/routing/Matcher/RedirectableUrlMatcher.php new file mode 100644 index 000000000..3770a9c24 --- /dev/null +++ b/lib/silex/vendor/symfony/routing/Matcher/RedirectableUrlMatcher.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\Routing\Matcher; + +use Symfony\Component\Routing\Exception\ResourceNotFoundException; +use Symfony\Component\Routing\Route; + +/** + * @author Fabien Potencier + */ +abstract class RedirectableUrlMatcher extends UrlMatcher implements RedirectableUrlMatcherInterface +{ + /** + * {@inheritdoc} + */ + public function match($pathinfo) + { + try { + $parameters = parent::match($pathinfo); + } catch (ResourceNotFoundException $e) { + if ('/' === substr($pathinfo, -1) || !in_array($this->context->getMethod(), array('HEAD', 'GET'))) { + throw $e; + } + + try { + $parameters = parent::match($pathinfo.'/'); + + return array_replace($parameters, $this->redirect($pathinfo.'/', isset($parameters['_route']) ? $parameters['_route'] : null)); + } catch (ResourceNotFoundException $e2) { + throw $e; + } + } + + return $parameters; + } + + /** + * {@inheritdoc} + */ + protected function handleRouteRequirements($pathinfo, $name, Route $route) + { + // expression condition + if ($route->getCondition() && !$this->getExpressionLanguage()->evaluate($route->getCondition(), array('context' => $this->context, 'request' => $this->request ?: $this->createRequest($pathinfo)))) { + return array(self::REQUIREMENT_MISMATCH, null); + } + + // check HTTP scheme requirement + $scheme = $this->context->getScheme(); + $schemes = $route->getSchemes(); + if ($schemes && !$route->hasScheme($scheme)) { + return array(self::ROUTE_MATCH, $this->redirect($pathinfo, $name, current($schemes))); + } + + return array(self::REQUIREMENT_MATCH, null); + } +} diff --git a/lib/silex/vendor/symfony/routing/Matcher/RedirectableUrlMatcherInterface.php b/lib/silex/vendor/symfony/routing/Matcher/RedirectableUrlMatcherInterface.php new file mode 100644 index 000000000..7c27bc879 --- /dev/null +++ b/lib/silex/vendor/symfony/routing/Matcher/RedirectableUrlMatcherInterface.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\Routing\Matcher; + +/** + * RedirectableUrlMatcherInterface knows how to redirect the user. + * + * @author Fabien Potencier + */ +interface RedirectableUrlMatcherInterface +{ + /** + * Redirects the user to another URL. + * + * @param string $path The path info to redirect to + * @param string $route The route name that matched + * @param string|null $scheme The URL scheme (null to keep the current one) + * + * @return array An array of parameters + */ + public function redirect($path, $route, $scheme = null); +} diff --git a/lib/silex/vendor/symfony/routing/Matcher/RequestMatcherInterface.php b/lib/silex/vendor/symfony/routing/Matcher/RequestMatcherInterface.php new file mode 100644 index 000000000..1eef778a5 --- /dev/null +++ b/lib/silex/vendor/symfony/routing/Matcher/RequestMatcherInterface.php @@ -0,0 +1,39 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Routing\Matcher; + +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\Routing\Exception\NoConfigurationException; +use Symfony\Component\Routing\Exception\ResourceNotFoundException; +use Symfony\Component\Routing\Exception\MethodNotAllowedException; + +/** + * RequestMatcherInterface is the interface that all request matcher classes must implement. + * + * @author Fabien Potencier + */ +interface RequestMatcherInterface +{ + /** + * Tries to match a request with a set of routes. + * + * If the matcher can not find information, it must throw one of the exceptions documented + * below. + * + * @return array An array of parameters + * + * @throws NoConfigurationException If no routing configuration could be found + * @throws ResourceNotFoundException If no matching resource could be found + * @throws MethodNotAllowedException If a matching resource was found but the request method is not allowed + */ + public function matchRequest(Request $request); +} diff --git a/lib/silex/vendor/symfony/routing/Matcher/TraceableUrlMatcher.php b/lib/silex/vendor/symfony/routing/Matcher/TraceableUrlMatcher.php new file mode 100644 index 000000000..9085be042 --- /dev/null +++ b/lib/silex/vendor/symfony/routing/Matcher/TraceableUrlMatcher.php @@ -0,0 +1,141 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Routing\Matcher; + +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\Routing\Exception\ExceptionInterface; +use Symfony\Component\Routing\Route; +use Symfony\Component\Routing\RouteCollection; + +/** + * TraceableUrlMatcher helps debug path info matching by tracing the match. + * + * @author Fabien Potencier + */ +class TraceableUrlMatcher extends UrlMatcher +{ + const ROUTE_DOES_NOT_MATCH = 0; + const ROUTE_ALMOST_MATCHES = 1; + const ROUTE_MATCHES = 2; + + protected $traces; + + public function getTraces($pathinfo) + { + $this->traces = array(); + + try { + $this->match($pathinfo); + } catch (ExceptionInterface $e) { + } + + return $this->traces; + } + + public function getTracesForRequest(Request $request) + { + $this->request = $request; + $traces = $this->getTraces($request->getPathInfo()); + $this->request = null; + + return $traces; + } + + protected function matchCollection($pathinfo, RouteCollection $routes) + { + foreach ($routes as $name => $route) { + $compiledRoute = $route->compile(); + + if (!preg_match($compiledRoute->getRegex(), $pathinfo, $matches)) { + // does it match without any requirements? + $r = new Route($route->getPath(), $route->getDefaults(), array(), $route->getOptions()); + $cr = $r->compile(); + if (!preg_match($cr->getRegex(), $pathinfo)) { + $this->addTrace(sprintf('Path "%s" does not match', $route->getPath()), self::ROUTE_DOES_NOT_MATCH, $name, $route); + + continue; + } + + foreach ($route->getRequirements() as $n => $regex) { + $r = new Route($route->getPath(), $route->getDefaults(), array($n => $regex), $route->getOptions()); + $cr = $r->compile(); + + if (in_array($n, $cr->getVariables()) && !preg_match($cr->getRegex(), $pathinfo)) { + $this->addTrace(sprintf('Requirement for "%s" does not match (%s)', $n, $regex), self::ROUTE_ALMOST_MATCHES, $name, $route); + + continue 2; + } + } + + continue; + } + + // check host requirement + $hostMatches = array(); + if ($compiledRoute->getHostRegex() && !preg_match($compiledRoute->getHostRegex(), $this->context->getHost(), $hostMatches)) { + $this->addTrace(sprintf('Host "%s" does not match the requirement ("%s")', $this->context->getHost(), $route->getHost()), self::ROUTE_ALMOST_MATCHES, $name, $route); + + continue; + } + + // check HTTP method requirement + if ($requiredMethods = $route->getMethods()) { + // HEAD and GET are equivalent as per RFC + if ('HEAD' === $method = $this->context->getMethod()) { + $method = 'GET'; + } + + if (!in_array($method, $requiredMethods)) { + $this->allow = array_merge($this->allow, $requiredMethods); + + $this->addTrace(sprintf('Method "%s" does not match any of the required methods (%s)', $this->context->getMethod(), implode(', ', $requiredMethods)), self::ROUTE_ALMOST_MATCHES, $name, $route); + + continue; + } + } + + // check condition + if ($condition = $route->getCondition()) { + if (!$this->getExpressionLanguage()->evaluate($condition, array('context' => $this->context, 'request' => $this->request ?: $this->createRequest($pathinfo)))) { + $this->addTrace(sprintf('Condition "%s" does not evaluate to "true"', $condition), self::ROUTE_ALMOST_MATCHES, $name, $route); + + continue; + } + } + + // check HTTP scheme requirement + if ($requiredSchemes = $route->getSchemes()) { + $scheme = $this->context->getScheme(); + + if (!$route->hasScheme($scheme)) { + $this->addTrace(sprintf('Scheme "%s" does not match any of the required schemes (%s); the user will be redirected to first required scheme', $scheme, implode(', ', $requiredSchemes)), self::ROUTE_ALMOST_MATCHES, $name, $route); + + return true; + } + } + + $this->addTrace('Route matches!', self::ROUTE_MATCHES, $name, $route); + + return true; + } + } + + private function addTrace($log, $level = self::ROUTE_DOES_NOT_MATCH, $name = null, $route = null) + { + $this->traces[] = array( + 'log' => $log, + 'name' => $name, + 'level' => $level, + 'path' => null !== $route ? $route->getPath() : null, + ); + } +} diff --git a/lib/silex/vendor/symfony/routing/Matcher/UrlMatcher.php b/lib/silex/vendor/symfony/routing/Matcher/UrlMatcher.php new file mode 100644 index 000000000..3cba7e66f --- /dev/null +++ b/lib/silex/vendor/symfony/routing/Matcher/UrlMatcher.php @@ -0,0 +1,250 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Routing\Matcher; + +use Symfony\Component\Routing\Exception\MethodNotAllowedException; +use Symfony\Component\Routing\Exception\NoConfigurationException; +use Symfony\Component\Routing\Exception\ResourceNotFoundException; +use Symfony\Component\Routing\RouteCollection; +use Symfony\Component\Routing\RequestContext; +use Symfony\Component\Routing\Route; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\ExpressionLanguage\ExpressionLanguage; +use Symfony\Component\ExpressionLanguage\ExpressionFunctionProviderInterface; + +/** + * UrlMatcher matches URL based on a set of routes. + * + * @author Fabien Potencier + */ +class UrlMatcher implements UrlMatcherInterface, RequestMatcherInterface +{ + const REQUIREMENT_MATCH = 0; + const REQUIREMENT_MISMATCH = 1; + const ROUTE_MATCH = 2; + + protected $context; + protected $allow = array(); + protected $routes; + protected $request; + protected $expressionLanguage; + + /** + * @var ExpressionFunctionProviderInterface[] + */ + protected $expressionLanguageProviders = array(); + + public function __construct(RouteCollection $routes, RequestContext $context) + { + $this->routes = $routes; + $this->context = $context; + } + + /** + * {@inheritdoc} + */ + public function setContext(RequestContext $context) + { + $this->context = $context; + } + + /** + * {@inheritdoc} + */ + public function getContext() + { + return $this->context; + } + + /** + * {@inheritdoc} + */ + public function match($pathinfo) + { + $this->allow = array(); + + if ($ret = $this->matchCollection(rawurldecode($pathinfo), $this->routes)) { + return $ret; + } + + if (0 === count($this->routes) && '/' === $pathinfo) { + throw new NoConfigurationException(); + } + + throw 0 < count($this->allow) + ? new MethodNotAllowedException(array_unique($this->allow)) + : new ResourceNotFoundException(sprintf('No routes found for "%s".', $pathinfo)); + } + + /** + * {@inheritdoc} + */ + public function matchRequest(Request $request) + { + $this->request = $request; + + $ret = $this->match($request->getPathInfo()); + + $this->request = null; + + return $ret; + } + + public function addExpressionLanguageProvider(ExpressionFunctionProviderInterface $provider) + { + $this->expressionLanguageProviders[] = $provider; + } + + /** + * Tries to match a URL with a set of routes. + * + * @param string $pathinfo The path info to be parsed + * @param RouteCollection $routes The set of routes + * + * @return array An array of parameters + * + * @throws NoConfigurationException If no routing configuration could be found + * @throws ResourceNotFoundException If the resource could not be found + * @throws MethodNotAllowedException If the resource was found but the request method is not allowed + */ + protected function matchCollection($pathinfo, RouteCollection $routes) + { + foreach ($routes as $name => $route) { + $compiledRoute = $route->compile(); + + // check the static prefix of the URL first. Only use the more expensive preg_match when it matches + if ('' !== $compiledRoute->getStaticPrefix() && 0 !== strpos($pathinfo, $compiledRoute->getStaticPrefix())) { + continue; + } + + if (!preg_match($compiledRoute->getRegex(), $pathinfo, $matches)) { + continue; + } + + $hostMatches = array(); + if ($compiledRoute->getHostRegex() && !preg_match($compiledRoute->getHostRegex(), $this->context->getHost(), $hostMatches)) { + continue; + } + + // check HTTP method requirement + if ($requiredMethods = $route->getMethods()) { + // HEAD and GET are equivalent as per RFC + if ('HEAD' === $method = $this->context->getMethod()) { + $method = 'GET'; + } + + if (!in_array($method, $requiredMethods)) { + $this->allow = array_merge($this->allow, $requiredMethods); + + continue; + } + } + + $status = $this->handleRouteRequirements($pathinfo, $name, $route); + + if (self::REQUIREMENT_MISMATCH === $status[0]) { + continue; + } + + return $this->getAttributes($route, $name, array_replace($matches, $hostMatches, isset($status[1]) ? $status[1] : array())); + } + } + + /** + * Returns an array of values to use as request attributes. + * + * As this method requires the Route object, it is not available + * in matchers that do not have access to the matched Route instance + * (like the PHP and Apache matcher dumpers). + * + * @param Route $route The route we are matching against + * @param string $name The name of the route + * @param array $attributes An array of attributes from the matcher + * + * @return array An array of parameters + */ + protected function getAttributes(Route $route, $name, array $attributes) + { + $attributes['_route'] = $name; + + return $this->mergeDefaults($attributes, $route->getDefaults()); + } + + /** + * Handles specific route requirements. + * + * @param string $pathinfo The path + * @param string $name The route name + * @param Route $route The route + * + * @return array The first element represents the status, the second contains additional information + */ + protected function handleRouteRequirements($pathinfo, $name, Route $route) + { + // expression condition + if ($route->getCondition() && !$this->getExpressionLanguage()->evaluate($route->getCondition(), array('context' => $this->context, 'request' => $this->request ?: $this->createRequest($pathinfo)))) { + return array(self::REQUIREMENT_MISMATCH, null); + } + + // check HTTP scheme requirement + $scheme = $this->context->getScheme(); + $status = $route->getSchemes() && !$route->hasScheme($scheme) ? self::REQUIREMENT_MISMATCH : self::REQUIREMENT_MATCH; + + return array($status, null); + } + + /** + * Get merged default parameters. + * + * @param array $params The parameters + * @param array $defaults The defaults + * + * @return array Merged default parameters + */ + protected function mergeDefaults($params, $defaults) + { + foreach ($params as $key => $value) { + if (!is_int($key)) { + $defaults[$key] = $value; + } + } + + return $defaults; + } + + protected function getExpressionLanguage() + { + if (null === $this->expressionLanguage) { + if (!class_exists('Symfony\Component\ExpressionLanguage\ExpressionLanguage')) { + throw new \RuntimeException('Unable to use expressions as the Symfony ExpressionLanguage component is not installed.'); + } + $this->expressionLanguage = new ExpressionLanguage(null, $this->expressionLanguageProviders); + } + + return $this->expressionLanguage; + } + + /** + * @internal + */ + protected function createRequest($pathinfo) + { + if (!class_exists('Symfony\Component\HttpFoundation\Request')) { + return null; + } + + return Request::create($this->context->getScheme().'://'.$this->context->getHost().$this->context->getBaseUrl().$pathinfo, $this->context->getMethod(), $this->context->getParameters(), array(), array(), array( + 'SCRIPT_FILENAME' => $this->context->getBaseUrl(), + 'SCRIPT_NAME' => $this->context->getBaseUrl(), + )); + } +} diff --git a/lib/silex/vendor/symfony/routing/Matcher/UrlMatcherInterface.php b/lib/silex/vendor/symfony/routing/Matcher/UrlMatcherInterface.php new file mode 100644 index 000000000..2c7c3135b --- /dev/null +++ b/lib/silex/vendor/symfony/routing/Matcher/UrlMatcherInterface.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\Routing\Matcher; + +use Symfony\Component\Routing\Exception\NoConfigurationException; +use Symfony\Component\Routing\RequestContextAwareInterface; +use Symfony\Component\Routing\Exception\ResourceNotFoundException; +use Symfony\Component\Routing\Exception\MethodNotAllowedException; + +/** + * UrlMatcherInterface is the interface that all URL matcher classes must implement. + * + * @author Fabien Potencier + */ +interface UrlMatcherInterface extends RequestContextAwareInterface +{ + /** + * Tries to match a URL path with a set of routes. + * + * If the matcher can not find information, it must throw one of the exceptions documented + * below. + * + * @param string $pathinfo The path info to be parsed (raw format, i.e. not urldecoded) + * + * @return array An array of parameters + * + * @throws NoConfigurationException If no routing configuration could be found + * @throws ResourceNotFoundException If the resource could not be found + * @throws MethodNotAllowedException If the resource was found but the request method is not allowed + */ + public function match($pathinfo); +} diff --git a/lib/silex/vendor/symfony/routing/README.md b/lib/silex/vendor/symfony/routing/README.md new file mode 100644 index 000000000..88fb1fde5 --- /dev/null +++ b/lib/silex/vendor/symfony/routing/README.md @@ -0,0 +1,13 @@ +Routing Component +================= + +The Routing component maps an HTTP request to a set of configuration variables. + +Resources +--------- + + * [Documentation](https://symfony.com/doc/current/components/routing/index.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/lib/silex/vendor/symfony/routing/RequestContext.php b/lib/silex/vendor/symfony/routing/RequestContext.php new file mode 100644 index 000000000..d62a7766e --- /dev/null +++ b/lib/silex/vendor/symfony/routing/RequestContext.php @@ -0,0 +1,336 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Routing; + +use Symfony\Component\HttpFoundation\Request; + +/** + * Holds information about the current request. + * + * This class implements a fluent interface. + * + * @author Fabien Potencier + * @author Tobias Schultze + */ +class RequestContext +{ + private $baseUrl; + private $pathInfo; + private $method; + private $host; + private $scheme; + private $httpPort; + private $httpsPort; + private $queryString; + private $parameters = array(); + + /** + * @param string $baseUrl The base URL + * @param string $method The HTTP method + * @param string $host The HTTP host name + * @param string $scheme The HTTP scheme + * @param int $httpPort The HTTP port + * @param int $httpsPort The HTTPS port + * @param string $path The path + * @param string $queryString The query string + */ + public function __construct($baseUrl = '', $method = 'GET', $host = 'localhost', $scheme = 'http', $httpPort = 80, $httpsPort = 443, $path = '/', $queryString = '') + { + $this->setBaseUrl($baseUrl); + $this->setMethod($method); + $this->setHost($host); + $this->setScheme($scheme); + $this->setHttpPort($httpPort); + $this->setHttpsPort($httpsPort); + $this->setPathInfo($path); + $this->setQueryString($queryString); + } + + /** + * Updates the RequestContext information based on a HttpFoundation Request. + * + * @return $this + */ + public function fromRequest(Request $request) + { + $this->setBaseUrl($request->getBaseUrl()); + $this->setPathInfo($request->getPathInfo()); + $this->setMethod($request->getMethod()); + $this->setHost($request->getHost()); + $this->setScheme($request->getScheme()); + $this->setHttpPort($request->isSecure() ? $this->httpPort : $request->getPort()); + $this->setHttpsPort($request->isSecure() ? $request->getPort() : $this->httpsPort); + $this->setQueryString($request->server->get('QUERY_STRING', '')); + + return $this; + } + + /** + * Gets the base URL. + * + * @return string The base URL + */ + public function getBaseUrl() + { + return $this->baseUrl; + } + + /** + * Sets the base URL. + * + * @param string $baseUrl The base URL + * + * @return $this + */ + public function setBaseUrl($baseUrl) + { + $this->baseUrl = $baseUrl; + + return $this; + } + + /** + * Gets the path info. + * + * @return string The path info + */ + public function getPathInfo() + { + return $this->pathInfo; + } + + /** + * Sets the path info. + * + * @param string $pathInfo The path info + * + * @return $this + */ + public function setPathInfo($pathInfo) + { + $this->pathInfo = $pathInfo; + + return $this; + } + + /** + * Gets the HTTP method. + * + * The method is always an uppercased string. + * + * @return string The HTTP method + */ + public function getMethod() + { + return $this->method; + } + + /** + * Sets the HTTP method. + * + * @param string $method The HTTP method + * + * @return $this + */ + public function setMethod($method) + { + $this->method = strtoupper($method); + + return $this; + } + + /** + * Gets the HTTP host. + * + * The host is always lowercased because it must be treated case-insensitive. + * + * @return string The HTTP host + */ + public function getHost() + { + return $this->host; + } + + /** + * Sets the HTTP host. + * + * @param string $host The HTTP host + * + * @return $this + */ + public function setHost($host) + { + $this->host = strtolower($host); + + return $this; + } + + /** + * Gets the HTTP scheme. + * + * @return string The HTTP scheme + */ + public function getScheme() + { + return $this->scheme; + } + + /** + * Sets the HTTP scheme. + * + * @param string $scheme The HTTP scheme + * + * @return $this + */ + public function setScheme($scheme) + { + $this->scheme = strtolower($scheme); + + return $this; + } + + /** + * Gets the HTTP port. + * + * @return int The HTTP port + */ + public function getHttpPort() + { + return $this->httpPort; + } + + /** + * Sets the HTTP port. + * + * @param int $httpPort The HTTP port + * + * @return $this + */ + public function setHttpPort($httpPort) + { + $this->httpPort = (int) $httpPort; + + return $this; + } + + /** + * Gets the HTTPS port. + * + * @return int The HTTPS port + */ + public function getHttpsPort() + { + return $this->httpsPort; + } + + /** + * Sets the HTTPS port. + * + * @param int $httpsPort The HTTPS port + * + * @return $this + */ + public function setHttpsPort($httpsPort) + { + $this->httpsPort = (int) $httpsPort; + + return $this; + } + + /** + * Gets the query string. + * + * @return string The query string without the "?" + */ + public function getQueryString() + { + return $this->queryString; + } + + /** + * Sets the query string. + * + * @param string $queryString The query string (after "?") + * + * @return $this + */ + public function setQueryString($queryString) + { + // string cast to be fault-tolerant, accepting null + $this->queryString = (string) $queryString; + + return $this; + } + + /** + * Returns the parameters. + * + * @return array The parameters + */ + public function getParameters() + { + return $this->parameters; + } + + /** + * Sets the parameters. + * + * @param array $parameters The parameters + * + * @return $this + */ + public function setParameters(array $parameters) + { + $this->parameters = $parameters; + + return $this; + } + + /** + * Gets a parameter value. + * + * @param string $name A parameter name + * + * @return mixed The parameter value or null if nonexistent + */ + public function getParameter($name) + { + return isset($this->parameters[$name]) ? $this->parameters[$name] : null; + } + + /** + * Checks if a parameter value is set for the given parameter. + * + * @param string $name A parameter name + * + * @return bool True if the parameter value is set, false otherwise + */ + public function hasParameter($name) + { + return array_key_exists($name, $this->parameters); + } + + /** + * Sets a parameter value. + * + * @param string $name A parameter name + * @param mixed $parameter The parameter value + * + * @return $this + */ + public function setParameter($name, $parameter) + { + $this->parameters[$name] = $parameter; + + return $this; + } +} diff --git a/lib/silex/vendor/symfony/routing/RequestContextAwareInterface.php b/lib/silex/vendor/symfony/routing/RequestContextAwareInterface.php new file mode 100644 index 000000000..df5b9fcd4 --- /dev/null +++ b/lib/silex/vendor/symfony/routing/RequestContextAwareInterface.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Routing; + +interface RequestContextAwareInterface +{ + /** + * Sets the request context. + */ + public function setContext(RequestContext $context); + + /** + * Gets the request context. + * + * @return RequestContext The context + */ + public function getContext(); +} diff --git a/lib/silex/vendor/symfony/routing/Route.php b/lib/silex/vendor/symfony/routing/Route.php new file mode 100644 index 000000000..cd50ac827 --- /dev/null +++ b/lib/silex/vendor/symfony/routing/Route.php @@ -0,0 +1,558 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Routing; + +/** + * A Route describes a route and its parameters. + * + * @author Fabien Potencier + * @author Tobias Schultze + */ +class Route implements \Serializable +{ + private $path = '/'; + private $host = ''; + private $schemes = array(); + private $methods = array(); + private $defaults = array(); + private $requirements = array(); + private $options = array(); + private $condition = ''; + + /** + * @var null|CompiledRoute + */ + private $compiled; + + /** + * Constructor. + * + * Available options: + * + * * compiler_class: A class name able to compile this route instance (RouteCompiler by default) + * * utf8: Whether UTF-8 matching is enforced ot not + * + * @param string $path The path pattern to match + * @param array $defaults An array of default parameter values + * @param array $requirements An array of requirements for parameters (regexes) + * @param array $options An array of options + * @param string $host The host pattern to match + * @param string|string[] $schemes A required URI scheme or an array of restricted schemes + * @param string|string[] $methods A required HTTP method or an array of restricted methods + * @param string $condition A condition that should evaluate to true for the route to match + */ + public function __construct($path, array $defaults = array(), array $requirements = array(), array $options = array(), $host = '', $schemes = array(), $methods = array(), $condition = '') + { + $this->setPath($path); + $this->setDefaults($defaults); + $this->setRequirements($requirements); + $this->setOptions($options); + $this->setHost($host); + $this->setSchemes($schemes); + $this->setMethods($methods); + $this->setCondition($condition); + } + + /** + * {@inheritdoc} + */ + public function serialize() + { + return serialize(array( + 'path' => $this->path, + 'host' => $this->host, + 'defaults' => $this->defaults, + 'requirements' => $this->requirements, + 'options' => $this->options, + 'schemes' => $this->schemes, + 'methods' => $this->methods, + 'condition' => $this->condition, + 'compiled' => $this->compiled, + )); + } + + /** + * {@inheritdoc} + */ + public function unserialize($serialized) + { + $data = unserialize($serialized); + $this->path = $data['path']; + $this->host = $data['host']; + $this->defaults = $data['defaults']; + $this->requirements = $data['requirements']; + $this->options = $data['options']; + $this->schemes = $data['schemes']; + $this->methods = $data['methods']; + + if (isset($data['condition'])) { + $this->condition = $data['condition']; + } + if (isset($data['compiled'])) { + $this->compiled = $data['compiled']; + } + } + + /** + * Returns the pattern for the path. + * + * @return string The path pattern + */ + public function getPath() + { + return $this->path; + } + + /** + * Sets the pattern for the path. + * + * This method implements a fluent interface. + * + * @param string $pattern The path pattern + * + * @return $this + */ + public function setPath($pattern) + { + // A pattern must start with a slash and must not have multiple slashes at the beginning because the + // generated path for this route would be confused with a network path, e.g. '//domain.com/path'. + $this->path = '/'.ltrim(trim($pattern), '/'); + $this->compiled = null; + + return $this; + } + + /** + * Returns the pattern for the host. + * + * @return string The host pattern + */ + public function getHost() + { + return $this->host; + } + + /** + * Sets the pattern for the host. + * + * This method implements a fluent interface. + * + * @param string $pattern The host pattern + * + * @return $this + */ + public function setHost($pattern) + { + $this->host = (string) $pattern; + $this->compiled = null; + + return $this; + } + + /** + * Returns the lowercased schemes this route is restricted to. + * So an empty array means that any scheme is allowed. + * + * @return string[] The schemes + */ + public function getSchemes() + { + return $this->schemes; + } + + /** + * Sets the schemes (e.g. 'https') this route is restricted to. + * So an empty array means that any scheme is allowed. + * + * This method implements a fluent interface. + * + * @param string|string[] $schemes The scheme or an array of schemes + * + * @return $this + */ + public function setSchemes($schemes) + { + $this->schemes = array_map('strtolower', (array) $schemes); + $this->compiled = null; + + return $this; + } + + /** + * Checks if a scheme requirement has been set. + * + * @param string $scheme + * + * @return bool true if the scheme requirement exists, otherwise false + */ + public function hasScheme($scheme) + { + return in_array(strtolower($scheme), $this->schemes, true); + } + + /** + * Returns the uppercased HTTP methods this route is restricted to. + * So an empty array means that any method is allowed. + * + * @return string[] The methods + */ + public function getMethods() + { + return $this->methods; + } + + /** + * Sets the HTTP methods (e.g. 'POST') this route is restricted to. + * So an empty array means that any method is allowed. + * + * This method implements a fluent interface. + * + * @param string|string[] $methods The method or an array of methods + * + * @return $this + */ + public function setMethods($methods) + { + $this->methods = array_map('strtoupper', (array) $methods); + $this->compiled = null; + + return $this; + } + + /** + * Returns the options. + * + * @return array The options + */ + public function getOptions() + { + return $this->options; + } + + /** + * Sets the options. + * + * This method implements a fluent interface. + * + * @param array $options The options + * + * @return $this + */ + public function setOptions(array $options) + { + $this->options = array( + 'compiler_class' => 'Symfony\\Component\\Routing\\RouteCompiler', + ); + + return $this->addOptions($options); + } + + /** + * Adds options. + * + * This method implements a fluent interface. + * + * @param array $options The options + * + * @return $this + */ + public function addOptions(array $options) + { + foreach ($options as $name => $option) { + $this->options[$name] = $option; + } + $this->compiled = null; + + return $this; + } + + /** + * Sets an option value. + * + * This method implements a fluent interface. + * + * @param string $name An option name + * @param mixed $value The option value + * + * @return $this + */ + public function setOption($name, $value) + { + $this->options[$name] = $value; + $this->compiled = null; + + return $this; + } + + /** + * Get an option value. + * + * @param string $name An option name + * + * @return mixed The option value or null when not given + */ + public function getOption($name) + { + return isset($this->options[$name]) ? $this->options[$name] : null; + } + + /** + * Checks if an option has been set. + * + * @param string $name An option name + * + * @return bool true if the option is set, false otherwise + */ + public function hasOption($name) + { + return array_key_exists($name, $this->options); + } + + /** + * Returns the defaults. + * + * @return array The defaults + */ + public function getDefaults() + { + return $this->defaults; + } + + /** + * Sets the defaults. + * + * This method implements a fluent interface. + * + * @param array $defaults The defaults + * + * @return $this + */ + public function setDefaults(array $defaults) + { + $this->defaults = array(); + + return $this->addDefaults($defaults); + } + + /** + * Adds defaults. + * + * This method implements a fluent interface. + * + * @param array $defaults The defaults + * + * @return $this + */ + public function addDefaults(array $defaults) + { + foreach ($defaults as $name => $default) { + $this->defaults[$name] = $default; + } + $this->compiled = null; + + return $this; + } + + /** + * Gets a default value. + * + * @param string $name A variable name + * + * @return mixed The default value or null when not given + */ + public function getDefault($name) + { + return isset($this->defaults[$name]) ? $this->defaults[$name] : null; + } + + /** + * Checks if a default value is set for the given variable. + * + * @param string $name A variable name + * + * @return bool true if the default value is set, false otherwise + */ + public function hasDefault($name) + { + return array_key_exists($name, $this->defaults); + } + + /** + * Sets a default value. + * + * @param string $name A variable name + * @param mixed $default The default value + * + * @return $this + */ + public function setDefault($name, $default) + { + $this->defaults[$name] = $default; + $this->compiled = null; + + return $this; + } + + /** + * Returns the requirements. + * + * @return array The requirements + */ + public function getRequirements() + { + return $this->requirements; + } + + /** + * Sets the requirements. + * + * This method implements a fluent interface. + * + * @param array $requirements The requirements + * + * @return $this + */ + public function setRequirements(array $requirements) + { + $this->requirements = array(); + + return $this->addRequirements($requirements); + } + + /** + * Adds requirements. + * + * This method implements a fluent interface. + * + * @param array $requirements The requirements + * + * @return $this + */ + public function addRequirements(array $requirements) + { + foreach ($requirements as $key => $regex) { + $this->requirements[$key] = $this->sanitizeRequirement($key, $regex); + } + $this->compiled = null; + + return $this; + } + + /** + * Returns the requirement for the given key. + * + * @param string $key The key + * + * @return string|null The regex or null when not given + */ + public function getRequirement($key) + { + return isset($this->requirements[$key]) ? $this->requirements[$key] : null; + } + + /** + * Checks if a requirement is set for the given key. + * + * @param string $key A variable name + * + * @return bool true if a requirement is specified, false otherwise + */ + public function hasRequirement($key) + { + return array_key_exists($key, $this->requirements); + } + + /** + * Sets a requirement for the given key. + * + * @param string $key The key + * @param string $regex The regex + * + * @return $this + */ + public function setRequirement($key, $regex) + { + $this->requirements[$key] = $this->sanitizeRequirement($key, $regex); + $this->compiled = null; + + return $this; + } + + /** + * Returns the condition. + * + * @return string The condition + */ + public function getCondition() + { + return $this->condition; + } + + /** + * Sets the condition. + * + * This method implements a fluent interface. + * + * @param string $condition The condition + * + * @return $this + */ + public function setCondition($condition) + { + $this->condition = (string) $condition; + $this->compiled = null; + + return $this; + } + + /** + * Compiles the route. + * + * @return CompiledRoute A CompiledRoute instance + * + * @throws \LogicException If the Route cannot be compiled because the + * path or host pattern is invalid + * + * @see RouteCompiler which is responsible for the compilation process + */ + public function compile() + { + if (null !== $this->compiled) { + return $this->compiled; + } + + $class = $this->getOption('compiler_class'); + + return $this->compiled = $class::compile($this); + } + + private function sanitizeRequirement($key, $regex) + { + if (!is_string($regex)) { + throw new \InvalidArgumentException(sprintf('Routing requirement for "%s" must be a string.', $key)); + } + + if ('' !== $regex && '^' === $regex[0]) { + $regex = (string) substr($regex, 1); // returns false for a single character + } + + if ('$' === substr($regex, -1)) { + $regex = substr($regex, 0, -1); + } + + if ('' === $regex) { + throw new \InvalidArgumentException(sprintf('Routing requirement for "%s" cannot be empty.', $key)); + } + + return $regex; + } +} diff --git a/lib/silex/vendor/symfony/routing/RouteCollection.php b/lib/silex/vendor/symfony/routing/RouteCollection.php new file mode 100644 index 000000000..feabf234b --- /dev/null +++ b/lib/silex/vendor/symfony/routing/RouteCollection.php @@ -0,0 +1,280 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Routing; + +use Symfony\Component\Config\Resource\ResourceInterface; + +/** + * A RouteCollection represents a set of Route instances. + * + * When adding a route at the end of the collection, an existing route + * with the same name is removed first. So there can only be one route + * with a given name. + * + * @author Fabien Potencier + * @author Tobias Schultze + */ +class RouteCollection implements \IteratorAggregate, \Countable +{ + /** + * @var Route[] + */ + private $routes = array(); + + /** + * @var array + */ + private $resources = array(); + + public function __clone() + { + foreach ($this->routes as $name => $route) { + $this->routes[$name] = clone $route; + } + } + + /** + * Gets the current RouteCollection as an Iterator that includes all routes. + * + * It implements \IteratorAggregate. + * + * @see all() + * + * @return \ArrayIterator|Route[] An \ArrayIterator object for iterating over routes + */ + public function getIterator() + { + return new \ArrayIterator($this->routes); + } + + /** + * Gets the number of Routes in this collection. + * + * @return int The number of routes + */ + public function count() + { + return count($this->routes); + } + + /** + * Adds a route. + * + * @param string $name The route name + * @param Route $route A Route instance + */ + public function add($name, Route $route) + { + unset($this->routes[$name]); + + $this->routes[$name] = $route; + } + + /** + * Returns all routes in this collection. + * + * @return Route[] An array of routes + */ + public function all() + { + return $this->routes; + } + + /** + * Gets a route by name. + * + * @param string $name The route name + * + * @return Route|null A Route instance or null when not found + */ + public function get($name) + { + return isset($this->routes[$name]) ? $this->routes[$name] : null; + } + + /** + * Removes a route or an array of routes by name from the collection. + * + * @param string|string[] $name The route name or an array of route names + */ + public function remove($name) + { + foreach ((array) $name as $n) { + unset($this->routes[$n]); + } + } + + /** + * Adds a route collection at the end of the current set by appending all + * routes of the added collection. + */ + public function addCollection(RouteCollection $collection) + { + // we need to remove all routes with the same names first because just replacing them + // would not place the new route at the end of the merged array + foreach ($collection->all() as $name => $route) { + unset($this->routes[$name]); + $this->routes[$name] = $route; + } + + foreach ($collection->getResources() as $resource) { + $this->addResource($resource); + } + } + + /** + * Adds a prefix to the path of all child routes. + * + * @param string $prefix An optional prefix to add before each pattern of the route collection + * @param array $defaults An array of default values + * @param array $requirements An array of requirements + */ + public function addPrefix($prefix, array $defaults = array(), array $requirements = array()) + { + $prefix = trim(trim($prefix), '/'); + + if ('' === $prefix) { + return; + } + + foreach ($this->routes as $route) { + $route->setPath('/'.$prefix.$route->getPath()); + $route->addDefaults($defaults); + $route->addRequirements($requirements); + } + } + + /** + * Sets the host pattern on all routes. + * + * @param string $pattern The pattern + * @param array $defaults An array of default values + * @param array $requirements An array of requirements + */ + public function setHost($pattern, array $defaults = array(), array $requirements = array()) + { + foreach ($this->routes as $route) { + $route->setHost($pattern); + $route->addDefaults($defaults); + $route->addRequirements($requirements); + } + } + + /** + * Sets a condition on all routes. + * + * Existing conditions will be overridden. + * + * @param string $condition The condition + */ + public function setCondition($condition) + { + foreach ($this->routes as $route) { + $route->setCondition($condition); + } + } + + /** + * Adds defaults to all routes. + * + * An existing default value under the same name in a route will be overridden. + * + * @param array $defaults An array of default values + */ + public function addDefaults(array $defaults) + { + if ($defaults) { + foreach ($this->routes as $route) { + $route->addDefaults($defaults); + } + } + } + + /** + * Adds requirements to all routes. + * + * An existing requirement under the same name in a route will be overridden. + * + * @param array $requirements An array of requirements + */ + public function addRequirements(array $requirements) + { + if ($requirements) { + foreach ($this->routes as $route) { + $route->addRequirements($requirements); + } + } + } + + /** + * Adds options to all routes. + * + * An existing option value under the same name in a route will be overridden. + * + * @param array $options An array of options + */ + public function addOptions(array $options) + { + if ($options) { + foreach ($this->routes as $route) { + $route->addOptions($options); + } + } + } + + /** + * Sets the schemes (e.g. 'https') all child routes are restricted to. + * + * @param string|string[] $schemes The scheme or an array of schemes + */ + public function setSchemes($schemes) + { + foreach ($this->routes as $route) { + $route->setSchemes($schemes); + } + } + + /** + * Sets the HTTP methods (e.g. 'POST') all child routes are restricted to. + * + * @param string|string[] $methods The method or an array of methods + */ + public function setMethods($methods) + { + foreach ($this->routes as $route) { + $route->setMethods($methods); + } + } + + /** + * Returns an array of resources loaded to build this collection. + * + * @return ResourceInterface[] An array of resources + */ + public function getResources() + { + return array_values($this->resources); + } + + /** + * Adds a resource for this collection. If the resource already exists + * it is not added. + */ + public function addResource(ResourceInterface $resource) + { + $key = (string) $resource; + + if (!isset($this->resources[$key])) { + $this->resources[$key] = $resource; + } + } +} diff --git a/lib/silex/vendor/symfony/routing/RouteCollectionBuilder.php b/lib/silex/vendor/symfony/routing/RouteCollectionBuilder.php new file mode 100644 index 000000000..e8a9a165d --- /dev/null +++ b/lib/silex/vendor/symfony/routing/RouteCollectionBuilder.php @@ -0,0 +1,380 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Routing; + +use Symfony\Component\Config\Exception\FileLoaderLoadException; +use Symfony\Component\Config\Loader\LoaderInterface; +use Symfony\Component\Config\Resource\ResourceInterface; + +/** + * Helps add and import routes into a RouteCollection. + * + * @author Ryan Weaver + */ +class RouteCollectionBuilder +{ + /** + * @var Route[]|RouteCollectionBuilder[] + */ + private $routes = array(); + + private $loader; + private $defaults = array(); + private $prefix; + private $host; + private $condition; + private $requirements = array(); + private $options = array(); + private $schemes; + private $methods; + private $resources = array(); + + public function __construct(LoaderInterface $loader = null) + { + $this->loader = $loader; + } + + /** + * Import an external routing resource and returns the RouteCollectionBuilder. + * + * $routes->import('blog.yml', '/blog'); + * + * @param mixed $resource + * @param string|null $prefix + * @param string $type + * + * @return self + * + * @throws FileLoaderLoadException + */ + public function import($resource, $prefix = '/', $type = null) + { + /** @var RouteCollection[] $collection */ + $collections = $this->load($resource, $type); + + // create a builder from the RouteCollection + $builder = $this->createBuilder(); + + foreach ($collections as $collection) { + if (null === $collection) { + continue; + } + + foreach ($collection->all() as $name => $route) { + $builder->addRoute($route, $name); + } + + foreach ($collection->getResources() as $resource) { + $builder->addResource($resource); + } + } + + // mount into this builder + $this->mount($prefix, $builder); + + return $builder; + } + + /** + * Adds a route and returns it for future modification. + * + * @param string $path The route path + * @param string $controller The route's controller + * @param string|null $name The name to give this route + * + * @return Route + */ + public function add($path, $controller, $name = null) + { + $route = new Route($path); + $route->setDefault('_controller', $controller); + $this->addRoute($route, $name); + + return $route; + } + + /** + * Returns a RouteCollectionBuilder that can be configured and then added with mount(). + * + * @return self + */ + public function createBuilder() + { + return new self($this->loader); + } + + /** + * Add a RouteCollectionBuilder. + * + * @param string $prefix + * @param RouteCollectionBuilder $builder + */ + public function mount($prefix, RouteCollectionBuilder $builder) + { + $builder->prefix = trim(trim($prefix), '/'); + $this->routes[] = $builder; + } + + /** + * Adds a Route object to the builder. + * + * @param Route $route + * @param string|null $name + * + * @return $this + */ + public function addRoute(Route $route, $name = null) + { + if (null === $name) { + // used as a flag to know which routes will need a name later + $name = '_unnamed_route_'.spl_object_hash($route); + } + + $this->routes[$name] = $route; + + return $this; + } + + /** + * Sets the host on all embedded routes (unless already set). + * + * @param string $pattern + * + * @return $this + */ + public function setHost($pattern) + { + $this->host = $pattern; + + return $this; + } + + /** + * Sets a condition on all embedded routes (unless already set). + * + * @param string $condition + * + * @return $this + */ + public function setCondition($condition) + { + $this->condition = $condition; + + return $this; + } + + /** + * Sets a default value that will be added to all embedded routes (unless that + * default value is already set). + * + * @param string $key + * @param mixed $value + * + * @return $this + */ + public function setDefault($key, $value) + { + $this->defaults[$key] = $value; + + return $this; + } + + /** + * Sets a requirement that will be added to all embedded routes (unless that + * requirement is already set). + * + * @param string $key + * @param mixed $regex + * + * @return $this + */ + public function setRequirement($key, $regex) + { + $this->requirements[$key] = $regex; + + return $this; + } + + /** + * Sets an option that will be added to all embedded routes (unless that + * option is already set). + * + * @param string $key + * @param mixed $value + * + * @return $this + */ + public function setOption($key, $value) + { + $this->options[$key] = $value; + + return $this; + } + + /** + * Sets the schemes on all embedded routes (unless already set). + * + * @param array|string $schemes + * + * @return $this + */ + public function setSchemes($schemes) + { + $this->schemes = $schemes; + + return $this; + } + + /** + * Sets the methods on all embedded routes (unless already set). + * + * @param array|string $methods + * + * @return $this + */ + public function setMethods($methods) + { + $this->methods = $methods; + + return $this; + } + + /** + * Adds a resource for this collection. + * + * @param ResourceInterface $resource + * + * @return $this + */ + private function addResource(ResourceInterface $resource) + { + $this->resources[] = $resource; + + return $this; + } + + /** + * Creates the final RouteCollection and returns it. + * + * @return RouteCollection + */ + public function build() + { + $routeCollection = new RouteCollection(); + + foreach ($this->routes as $name => $route) { + if ($route instanceof Route) { + $route->setDefaults(array_merge($this->defaults, $route->getDefaults())); + $route->setOptions(array_merge($this->options, $route->getOptions())); + + foreach ($this->requirements as $key => $val) { + if (!$route->hasRequirement($key)) { + $route->setRequirement($key, $val); + } + } + + if (null !== $this->prefix) { + $route->setPath('/'.$this->prefix.$route->getPath()); + } + + if (!$route->getHost()) { + $route->setHost($this->host); + } + + if (!$route->getCondition()) { + $route->setCondition($this->condition); + } + + if (!$route->getSchemes()) { + $route->setSchemes($this->schemes); + } + + if (!$route->getMethods()) { + $route->setMethods($this->methods); + } + + // auto-generate the route name if it's been marked + if ('_unnamed_route_' === substr($name, 0, 15)) { + $name = $this->generateRouteName($route); + } + + $routeCollection->add($name, $route); + } else { + /* @var self $route */ + $subCollection = $route->build(); + $subCollection->addPrefix($this->prefix); + + $routeCollection->addCollection($subCollection); + } + } + + foreach ($this->resources as $resource) { + $routeCollection->addResource($resource); + } + + return $routeCollection; + } + + /** + * Generates a route name based on details of this route. + * + * @return string + */ + private function generateRouteName(Route $route) + { + $methods = implode('_', $route->getMethods()).'_'; + + $routeName = $methods.$route->getPath(); + $routeName = str_replace(array('/', ':', '|', '-'), '_', $routeName); + $routeName = preg_replace('/[^a-z0-9A-Z_.]+/', '', $routeName); + + // Collapse consecutive underscores down into a single underscore. + $routeName = preg_replace('/_+/', '_', $routeName); + + return $routeName; + } + + /** + * Finds a loader able to load an imported resource and loads it. + * + * @param mixed $resource A resource + * @param string|null $type The resource type or null if unknown + * + * @return RouteCollection[] + * + * @throws FileLoaderLoadException If no loader is found + */ + private function load($resource, $type = null) + { + if (null === $this->loader) { + throw new \BadMethodCallException('Cannot import other routing resources: you must pass a LoaderInterface when constructing RouteCollectionBuilder.'); + } + + if ($this->loader->supports($resource, $type)) { + $collections = $this->loader->load($resource, $type); + + return is_array($collections) ? $collections : array($collections); + } + + if (null === $resolver = $this->loader->getResolver()) { + throw new FileLoaderLoadException($resource, null, null, null, $type); + } + + if (false === $loader = $resolver->resolve($resource, $type)) { + throw new FileLoaderLoadException($resource, null, null, null, $type); + } + + $collections = $loader->load($resource, $type); + + return is_array($collections) ? $collections : array($collections); + } +} diff --git a/lib/silex/vendor/symfony/routing/RouteCompiler.php b/lib/silex/vendor/symfony/routing/RouteCompiler.php new file mode 100644 index 000000000..9b5526f31 --- /dev/null +++ b/lib/silex/vendor/symfony/routing/RouteCompiler.php @@ -0,0 +1,316 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Routing; + +/** + * RouteCompiler compiles Route instances to CompiledRoute instances. + * + * @author Fabien Potencier + * @author Tobias Schultze + */ +class RouteCompiler implements RouteCompilerInterface +{ + const REGEX_DELIMITER = '#'; + + /** + * This string defines the characters that are automatically considered separators in front of + * optional placeholders (with default and no static text following). Such a single separator + * can be left out together with the optional placeholder from matching and generating URLs. + */ + const SEPARATORS = '/,;.:-_~+*=@|'; + + /** + * The maximum supported length of a PCRE subpattern name + * http://pcre.org/current/doc/html/pcre2pattern.html#SEC16. + * + * @internal + */ + const VARIABLE_MAXIMUM_LENGTH = 32; + + /** + * {@inheritdoc} + * + * @throws \InvalidArgumentException if a path variable is named _fragment + * @throws \LogicException if a variable is referenced more than once + * @throws \DomainException if a variable name starts with a digit or if it is too long to be successfully used as + * a PCRE subpattern + */ + public static function compile(Route $route) + { + $hostVariables = array(); + $variables = array(); + $hostRegex = null; + $hostTokens = array(); + + if ('' !== $host = $route->getHost()) { + $result = self::compilePattern($route, $host, true); + + $hostVariables = $result['variables']; + $variables = $hostVariables; + + $hostTokens = $result['tokens']; + $hostRegex = $result['regex']; + } + + $path = $route->getPath(); + + $result = self::compilePattern($route, $path, false); + + $staticPrefix = $result['staticPrefix']; + + $pathVariables = $result['variables']; + + foreach ($pathVariables as $pathParam) { + if ('_fragment' === $pathParam) { + throw new \InvalidArgumentException(sprintf('Route pattern "%s" cannot contain "_fragment" as a path parameter.', $route->getPath())); + } + } + + $variables = array_merge($variables, $pathVariables); + + $tokens = $result['tokens']; + $regex = $result['regex']; + + return new CompiledRoute( + $staticPrefix, + $regex, + $tokens, + $pathVariables, + $hostRegex, + $hostTokens, + $hostVariables, + array_unique($variables) + ); + } + + private static function compilePattern(Route $route, $pattern, $isHost) + { + $tokens = array(); + $variables = array(); + $matches = array(); + $pos = 0; + $defaultSeparator = $isHost ? '.' : '/'; + $useUtf8 = preg_match('//u', $pattern); + $needsUtf8 = $route->getOption('utf8'); + + if (!$needsUtf8 && $useUtf8 && preg_match('/[\x80-\xFF]/', $pattern)) { + $needsUtf8 = true; + @trigger_error(sprintf('Using UTF-8 route patterns without setting the "utf8" option is deprecated since Symfony 3.2 and will throw a LogicException in 4.0. Turn on the "utf8" route option for pattern "%s".', $pattern), E_USER_DEPRECATED); + } + if (!$useUtf8 && $needsUtf8) { + throw new \LogicException(sprintf('Cannot mix UTF-8 requirements with non-UTF-8 pattern "%s".', $pattern)); + } + + // Match all variables enclosed in "{}" and iterate over them. But we only want to match the innermost variable + // in case of nested "{}", e.g. {foo{bar}}. This in ensured because \w does not match "{" or "}" itself. + preg_match_all('#\{\w+\}#', $pattern, $matches, PREG_OFFSET_CAPTURE | PREG_SET_ORDER); + foreach ($matches as $match) { + $varName = substr($match[0][0], 1, -1); + // get all static text preceding the current variable + $precedingText = substr($pattern, $pos, $match[0][1] - $pos); + $pos = $match[0][1] + strlen($match[0][0]); + + if (!strlen($precedingText)) { + $precedingChar = ''; + } elseif ($useUtf8) { + preg_match('/.$/u', $precedingText, $precedingChar); + $precedingChar = $precedingChar[0]; + } else { + $precedingChar = substr($precedingText, -1); + } + $isSeparator = '' !== $precedingChar && false !== strpos(static::SEPARATORS, $precedingChar); + + // A PCRE subpattern name must start with a non-digit. Also a PHP variable cannot start with a digit so the + // variable would not be usable as a Controller action argument. + if (preg_match('/^\d/', $varName)) { + throw new \DomainException(sprintf('Variable name "%s" cannot start with a digit in route pattern "%s". Please use a different name.', $varName, $pattern)); + } + if (in_array($varName, $variables)) { + throw new \LogicException(sprintf('Route pattern "%s" cannot reference variable name "%s" more than once.', $pattern, $varName)); + } + + if (strlen($varName) > self::VARIABLE_MAXIMUM_LENGTH) { + throw new \DomainException(sprintf('Variable name "%s" cannot be longer than %s characters in route pattern "%s". Please use a shorter name.', $varName, self::VARIABLE_MAXIMUM_LENGTH, $pattern)); + } + + if ($isSeparator && $precedingText !== $precedingChar) { + $tokens[] = array('text', substr($precedingText, 0, -strlen($precedingChar))); + } elseif (!$isSeparator && strlen($precedingText) > 0) { + $tokens[] = array('text', $precedingText); + } + + $regexp = $route->getRequirement($varName); + if (null === $regexp) { + $followingPattern = (string) substr($pattern, $pos); + // Find the next static character after the variable that functions as a separator. By default, this separator and '/' + // are disallowed for the variable. This default requirement makes sure that optional variables can be matched at all + // and that the generating-matching-combination of URLs unambiguous, i.e. the params used for generating the URL are + // the same that will be matched. Example: new Route('/{page}.{_format}', array('_format' => 'html')) + // If {page} would also match the separating dot, {_format} would never match as {page} will eagerly consume everything. + // Also even if {_format} was not optional the requirement prevents that {page} matches something that was originally + // part of {_format} when generating the URL, e.g. _format = 'mobile.html'. + $nextSeparator = self::findNextSeparator($followingPattern, $useUtf8); + $regexp = sprintf( + '[^%s%s]+', + preg_quote($defaultSeparator, self::REGEX_DELIMITER), + $defaultSeparator !== $nextSeparator && '' !== $nextSeparator ? preg_quote($nextSeparator, self::REGEX_DELIMITER) : '' + ); + if (('' !== $nextSeparator && !preg_match('#^\{\w+\}#', $followingPattern)) || '' === $followingPattern) { + // When we have a separator, which is disallowed for the variable, we can optimize the regex with a possessive + // quantifier. This prevents useless backtracking of PCRE and improves performance by 20% for matching those patterns. + // Given the above example, there is no point in backtracking into {page} (that forbids the dot) when a dot must follow + // after it. This optimization cannot be applied when the next char is no real separator or when the next variable is + // directly adjacent, e.g. '/{x}{y}'. + $regexp .= '+'; + } + } else { + if (!preg_match('//u', $regexp)) { + $useUtf8 = false; + } elseif (!$needsUtf8 && preg_match('/[\x80-\xFF]|(?= 0; --$i) { + $token = $tokens[$i]; + if ('variable' === $token[0] && $route->hasDefault($token[3])) { + $firstOptional = $i; + } else { + break; + } + } + } + + // compute the matching regexp + $regexp = ''; + for ($i = 0, $nbToken = count($tokens); $i < $nbToken; ++$i) { + $regexp .= self::computeRegexp($tokens, $i, $firstOptional); + } + $regexp = self::REGEX_DELIMITER.'^'.$regexp.'$'.self::REGEX_DELIMITER.'s'.($isHost ? 'i' : ''); + + // enable Utf8 matching if really required + if ($needsUtf8) { + $regexp .= 'u'; + for ($i = 0, $nbToken = count($tokens); $i < $nbToken; ++$i) { + if ('variable' === $tokens[$i][0]) { + $tokens[$i][] = true; + } + } + } + + return array( + 'staticPrefix' => self::determineStaticPrefix($route, $tokens), + 'regex' => $regexp, + 'tokens' => array_reverse($tokens), + 'variables' => $variables, + ); + } + + /** + * Determines the longest static prefix possible for a route. + * + * @return string The leading static part of a route's path + */ + private static function determineStaticPrefix(Route $route, array $tokens) + { + if ('text' !== $tokens[0][0]) { + return ($route->hasDefault($tokens[0][3]) || '/' === $tokens[0][1]) ? '' : $tokens[0][1]; + } + + $prefix = $tokens[0][1]; + + if (isset($tokens[1][1]) && '/' !== $tokens[1][1] && false === $route->hasDefault($tokens[1][3])) { + $prefix .= $tokens[1][1]; + } + + return $prefix; + } + + /** + * Returns the next static character in the Route pattern that will serve as a separator. + * + * @param string $pattern The route pattern + * @param bool $useUtf8 Whether the character is encoded in UTF-8 or not + * + * @return string The next static character that functions as separator (or empty string when none available) + */ + private static function findNextSeparator($pattern, $useUtf8) + { + if ('' == $pattern) { + // return empty string if pattern is empty or false (false which can be returned by substr) + return ''; + } + // first remove all placeholders from the pattern so we can find the next real static character + if ('' === $pattern = preg_replace('#\{\w+\}#', '', $pattern)) { + return ''; + } + if ($useUtf8) { + preg_match('/^./u', $pattern, $pattern); + } + + return false !== strpos(static::SEPARATORS, $pattern[0]) ? $pattern[0] : ''; + } + + /** + * Computes the regexp used to match a specific token. It can be static text or a subpattern. + * + * @param array $tokens The route tokens + * @param int $index The index of the current token + * @param int $firstOptional The index of the first optional token + * + * @return string The regexp pattern for a single token + */ + private static function computeRegexp(array $tokens, $index, $firstOptional) + { + $token = $tokens[$index]; + if ('text' === $token[0]) { + // Text tokens + return preg_quote($token[1], self::REGEX_DELIMITER); + } else { + // Variable tokens + if (0 === $index && 0 === $firstOptional) { + // When the only token is an optional variable token, the separator is required + return sprintf('%s(?P<%s>%s)?', preg_quote($token[1], self::REGEX_DELIMITER), $token[3], $token[2]); + } else { + $regexp = sprintf('%s(?P<%s>%s)', preg_quote($token[1], self::REGEX_DELIMITER), $token[3], $token[2]); + if ($index >= $firstOptional) { + // Enclose each optional token in a subpattern to make it optional. + // "?:" means it is non-capturing, i.e. the portion of the subject string that + // matched the optional subpattern is not passed back. + $regexp = "(?:$regexp"; + $nbTokens = count($tokens); + if ($nbTokens - 1 == $index) { + // Close the optional subpatterns + $regexp .= str_repeat(')?', $nbTokens - $firstOptional - (0 === $firstOptional ? 1 : 0)); + } + } + + return $regexp; + } + } + } +} diff --git a/lib/silex/vendor/symfony/routing/RouteCompilerInterface.php b/lib/silex/vendor/symfony/routing/RouteCompilerInterface.php new file mode 100644 index 000000000..ddfa7ca49 --- /dev/null +++ b/lib/silex/vendor/symfony/routing/RouteCompilerInterface.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\Routing; + +/** + * RouteCompilerInterface is the interface that all RouteCompiler classes must implement. + * + * @author Fabien Potencier + */ +interface RouteCompilerInterface +{ + /** + * Compiles the current route instance. + * + * @return CompiledRoute A CompiledRoute instance + * + * @throws \LogicException If the Route cannot be compiled because the + * path or host pattern is invalid + */ + public static function compile(Route $route); +} diff --git a/lib/silex/vendor/symfony/routing/Router.php b/lib/silex/vendor/symfony/routing/Router.php new file mode 100644 index 000000000..0de921853 --- /dev/null +++ b/lib/silex/vendor/symfony/routing/Router.php @@ -0,0 +1,384 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Routing; + +use Symfony\Component\Config\Loader\LoaderInterface; +use Symfony\Component\Config\ConfigCacheInterface; +use Symfony\Component\Config\ConfigCacheFactoryInterface; +use Symfony\Component\Config\ConfigCacheFactory; +use Psr\Log\LoggerInterface; +use Symfony\Component\Routing\Generator\ConfigurableRequirementsInterface; +use Symfony\Component\Routing\Generator\UrlGeneratorInterface; +use Symfony\Component\Routing\Generator\Dumper\GeneratorDumperInterface; +use Symfony\Component\Routing\Matcher\RequestMatcherInterface; +use Symfony\Component\Routing\Matcher\UrlMatcherInterface; +use Symfony\Component\Routing\Matcher\Dumper\MatcherDumperInterface; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\ExpressionLanguage\ExpressionFunctionProviderInterface; + +/** + * The Router class is an example of the integration of all pieces of the + * routing system for easier use. + * + * @author Fabien Potencier + */ +class Router implements RouterInterface, RequestMatcherInterface +{ + /** + * @var UrlMatcherInterface|null + */ + protected $matcher; + + /** + * @var UrlGeneratorInterface|null + */ + protected $generator; + + /** + * @var RequestContext + */ + protected $context; + + /** + * @var LoaderInterface + */ + protected $loader; + + /** + * @var RouteCollection|null + */ + protected $collection; + + /** + * @var mixed + */ + protected $resource; + + /** + * @var array + */ + protected $options = array(); + + /** + * @var LoggerInterface|null + */ + protected $logger; + + /** + * @var ConfigCacheFactoryInterface|null + */ + private $configCacheFactory; + + /** + * @var ExpressionFunctionProviderInterface[] + */ + private $expressionLanguageProviders = array(); + + /** + * @param LoaderInterface $loader A LoaderInterface instance + * @param mixed $resource The main resource to load + * @param array $options An array of options + * @param RequestContext $context The context + * @param LoggerInterface $logger A logger instance + */ + public function __construct(LoaderInterface $loader, $resource, array $options = array(), RequestContext $context = null, LoggerInterface $logger = null) + { + $this->loader = $loader; + $this->resource = $resource; + $this->logger = $logger; + $this->context = $context ?: new RequestContext(); + $this->setOptions($options); + } + + /** + * Sets options. + * + * Available options: + * + * * cache_dir: The cache directory (or null to disable caching) + * * debug: Whether to enable debugging or not (false by default) + * * generator_class: The name of a UrlGeneratorInterface implementation + * * generator_base_class: The base class for the dumped generator class + * * generator_cache_class: The class name for the dumped generator class + * * generator_dumper_class: The name of a GeneratorDumperInterface implementation + * * matcher_class: The name of a UrlMatcherInterface implementation + * * matcher_base_class: The base class for the dumped matcher class + * * matcher_dumper_class: The class name for the dumped matcher class + * * matcher_cache_class: The name of a MatcherDumperInterface implementation + * * resource_type: Type hint for the main resource (optional) + * * strict_requirements: Configure strict requirement checking for generators + * implementing ConfigurableRequirementsInterface (default is true) + * + * @param array $options An array of options + * + * @throws \InvalidArgumentException When unsupported option is provided + */ + public function setOptions(array $options) + { + $this->options = array( + 'cache_dir' => null, + 'debug' => false, + 'generator_class' => 'Symfony\\Component\\Routing\\Generator\\UrlGenerator', + 'generator_base_class' => 'Symfony\\Component\\Routing\\Generator\\UrlGenerator', + 'generator_dumper_class' => 'Symfony\\Component\\Routing\\Generator\\Dumper\\PhpGeneratorDumper', + 'generator_cache_class' => 'ProjectUrlGenerator', + 'matcher_class' => 'Symfony\\Component\\Routing\\Matcher\\UrlMatcher', + 'matcher_base_class' => 'Symfony\\Component\\Routing\\Matcher\\UrlMatcher', + 'matcher_dumper_class' => 'Symfony\\Component\\Routing\\Matcher\\Dumper\\PhpMatcherDumper', + 'matcher_cache_class' => 'ProjectUrlMatcher', + 'resource_type' => null, + 'strict_requirements' => true, + ); + + // check option names and live merge, if errors are encountered Exception will be thrown + $invalid = array(); + foreach ($options as $key => $value) { + if (array_key_exists($key, $this->options)) { + $this->options[$key] = $value; + } else { + $invalid[] = $key; + } + } + + if ($invalid) { + throw new \InvalidArgumentException(sprintf('The Router does not support the following options: "%s".', implode('", "', $invalid))); + } + } + + /** + * Sets an option. + * + * @param string $key The key + * @param mixed $value The value + * + * @throws \InvalidArgumentException + */ + public function setOption($key, $value) + { + if (!array_key_exists($key, $this->options)) { + throw new \InvalidArgumentException(sprintf('The Router does not support the "%s" option.', $key)); + } + + $this->options[$key] = $value; + } + + /** + * Gets an option value. + * + * @param string $key The key + * + * @return mixed The value + * + * @throws \InvalidArgumentException + */ + public function getOption($key) + { + if (!array_key_exists($key, $this->options)) { + throw new \InvalidArgumentException(sprintf('The Router does not support the "%s" option.', $key)); + } + + return $this->options[$key]; + } + + /** + * {@inheritdoc} + */ + public function getRouteCollection() + { + if (null === $this->collection) { + $this->collection = $this->loader->load($this->resource, $this->options['resource_type']); + } + + return $this->collection; + } + + /** + * {@inheritdoc} + */ + public function setContext(RequestContext $context) + { + $this->context = $context; + + if (null !== $this->matcher) { + $this->getMatcher()->setContext($context); + } + if (null !== $this->generator) { + $this->getGenerator()->setContext($context); + } + } + + /** + * {@inheritdoc} + */ + public function getContext() + { + return $this->context; + } + + /** + * Sets the ConfigCache factory to use. + */ + public function setConfigCacheFactory(ConfigCacheFactoryInterface $configCacheFactory) + { + $this->configCacheFactory = $configCacheFactory; + } + + /** + * {@inheritdoc} + */ + public function generate($name, $parameters = array(), $referenceType = self::ABSOLUTE_PATH) + { + return $this->getGenerator()->generate($name, $parameters, $referenceType); + } + + /** + * {@inheritdoc} + */ + public function match($pathinfo) + { + return $this->getMatcher()->match($pathinfo); + } + + /** + * {@inheritdoc} + */ + public function matchRequest(Request $request) + { + $matcher = $this->getMatcher(); + if (!$matcher instanceof RequestMatcherInterface) { + // fallback to the default UrlMatcherInterface + return $matcher->match($request->getPathInfo()); + } + + return $matcher->matchRequest($request); + } + + /** + * Gets the UrlMatcher instance associated with this Router. + * + * @return UrlMatcherInterface A UrlMatcherInterface instance + */ + public function getMatcher() + { + if (null !== $this->matcher) { + return $this->matcher; + } + + if (null === $this->options['cache_dir'] || null === $this->options['matcher_cache_class']) { + $this->matcher = new $this->options['matcher_class']($this->getRouteCollection(), $this->context); + if (method_exists($this->matcher, 'addExpressionLanguageProvider')) { + foreach ($this->expressionLanguageProviders as $provider) { + $this->matcher->addExpressionLanguageProvider($provider); + } + } + + return $this->matcher; + } + + $cache = $this->getConfigCacheFactory()->cache($this->options['cache_dir'].'/'.$this->options['matcher_cache_class'].'.php', + function (ConfigCacheInterface $cache) { + $dumper = $this->getMatcherDumperInstance(); + if (method_exists($dumper, 'addExpressionLanguageProvider')) { + foreach ($this->expressionLanguageProviders as $provider) { + $dumper->addExpressionLanguageProvider($provider); + } + } + + $options = array( + 'class' => $this->options['matcher_cache_class'], + 'base_class' => $this->options['matcher_base_class'], + ); + + $cache->write($dumper->dump($options), $this->getRouteCollection()->getResources()); + } + ); + + require_once $cache->getPath(); + + return $this->matcher = new $this->options['matcher_cache_class']($this->context); + } + + /** + * Gets the UrlGenerator instance associated with this Router. + * + * @return UrlGeneratorInterface A UrlGeneratorInterface instance + */ + public function getGenerator() + { + if (null !== $this->generator) { + return $this->generator; + } + + if (null === $this->options['cache_dir'] || null === $this->options['generator_cache_class']) { + $this->generator = new $this->options['generator_class']($this->getRouteCollection(), $this->context, $this->logger); + } else { + $cache = $this->getConfigCacheFactory()->cache($this->options['cache_dir'].'/'.$this->options['generator_cache_class'].'.php', + function (ConfigCacheInterface $cache) { + $dumper = $this->getGeneratorDumperInstance(); + + $options = array( + 'class' => $this->options['generator_cache_class'], + 'base_class' => $this->options['generator_base_class'], + ); + + $cache->write($dumper->dump($options), $this->getRouteCollection()->getResources()); + } + ); + + require_once $cache->getPath(); + + $this->generator = new $this->options['generator_cache_class']($this->context, $this->logger); + } + + if ($this->generator instanceof ConfigurableRequirementsInterface) { + $this->generator->setStrictRequirements($this->options['strict_requirements']); + } + + return $this->generator; + } + + public function addExpressionLanguageProvider(ExpressionFunctionProviderInterface $provider) + { + $this->expressionLanguageProviders[] = $provider; + } + + /** + * @return GeneratorDumperInterface + */ + protected function getGeneratorDumperInstance() + { + return new $this->options['generator_dumper_class']($this->getRouteCollection()); + } + + /** + * @return MatcherDumperInterface + */ + protected function getMatcherDumperInstance() + { + return new $this->options['matcher_dumper_class']($this->getRouteCollection()); + } + + /** + * Provides the ConfigCache factory implementation, falling back to a + * default implementation if necessary. + * + * @return ConfigCacheFactoryInterface $configCacheFactory + */ + private function getConfigCacheFactory() + { + if (null === $this->configCacheFactory) { + $this->configCacheFactory = new ConfigCacheFactory($this->options['debug']); + } + + return $this->configCacheFactory; + } +} diff --git a/lib/silex/vendor/symfony/routing/RouterInterface.php b/lib/silex/vendor/symfony/routing/RouterInterface.php new file mode 100644 index 000000000..a10ae34e0 --- /dev/null +++ b/lib/silex/vendor/symfony/routing/RouterInterface.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\Routing; + +use Symfony\Component\Routing\Generator\UrlGeneratorInterface; +use Symfony\Component\Routing\Matcher\UrlMatcherInterface; + +/** + * RouterInterface is the interface that all Router classes must implement. + * + * This interface is the concatenation of UrlMatcherInterface and UrlGeneratorInterface. + * + * @author Fabien Potencier + */ +interface RouterInterface extends UrlMatcherInterface, UrlGeneratorInterface +{ + /** + * Gets the RouteCollection instance associated with this Router. + * + * @return RouteCollection A RouteCollection instance + */ + public function getRouteCollection(); +} diff --git a/lib/silex/vendor/symfony/routing/Tests/Annotation/RouteTest.php b/lib/silex/vendor/symfony/routing/Tests/Annotation/RouteTest.php new file mode 100644 index 000000000..9af22f29f --- /dev/null +++ b/lib/silex/vendor/symfony/routing/Tests/Annotation/RouteTest.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\Routing\Tests\Annotation; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Routing\Annotation\Route; + +class RouteTest extends TestCase +{ + /** + * @expectedException \BadMethodCallException + */ + public function testInvalidRouteParameter() + { + $route = new Route(array('foo' => 'bar')); + } + + /** + * @dataProvider getValidParameters + */ + public function testRouteParameters($parameter, $value, $getter) + { + $route = new Route(array($parameter => $value)); + $this->assertEquals($route->$getter(), $value); + } + + public function getValidParameters() + { + return array( + array('value', '/Blog', 'getPath'), + array('requirements', array('locale' => 'en'), 'getRequirements'), + array('options', array('compiler_class' => 'RouteCompiler'), 'getOptions'), + array('name', 'blog_index', 'getName'), + array('defaults', array('_controller' => 'MyBlogBundle:Blog:index'), 'getDefaults'), + array('schemes', array('https'), 'getSchemes'), + array('methods', array('GET', 'POST'), 'getMethods'), + array('host', '{locale}.example.com', 'getHost'), + array('condition', 'context.getMethod() == "GET"', 'getCondition'), + ); + } +} diff --git a/lib/silex/vendor/symfony/routing/Tests/CompiledRouteTest.php b/lib/silex/vendor/symfony/routing/Tests/CompiledRouteTest.php new file mode 100644 index 000000000..5bec7bbd7 --- /dev/null +++ b/lib/silex/vendor/symfony/routing/Tests/CompiledRouteTest.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Routing\Tests; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Routing\CompiledRoute; + +class CompiledRouteTest extends TestCase +{ + public function testAccessors() + { + $compiled = new CompiledRoute('prefix', 'regex', array('tokens'), array(), null, array(), array(), array('variables')); + $this->assertEquals('prefix', $compiled->getStaticPrefix(), '__construct() takes a static prefix as its second argument'); + $this->assertEquals('regex', $compiled->getRegex(), '__construct() takes a regexp as its third argument'); + $this->assertEquals(array('tokens'), $compiled->getTokens(), '__construct() takes an array of tokens as its fourth argument'); + $this->assertEquals(array('variables'), $compiled->getVariables(), '__construct() takes an array of variables as its ninth argument'); + } +} diff --git a/lib/silex/vendor/symfony/routing/Tests/DependencyInjection/RoutingResolverPassTest.php b/lib/silex/vendor/symfony/routing/Tests/DependencyInjection/RoutingResolverPassTest.php new file mode 100644 index 000000000..97a34c969 --- /dev/null +++ b/lib/silex/vendor/symfony/routing/Tests/DependencyInjection/RoutingResolverPassTest.php @@ -0,0 +1,36 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Routing\Tests\DependencyInjection; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Config\Loader\LoaderResolver; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\Routing\DependencyInjection\RoutingResolverPass; + +class RoutingResolverPassTest extends TestCase +{ + public function testProcess() + { + $container = new ContainerBuilder(); + $container->register('routing.resolver', LoaderResolver::class); + $container->register('loader1')->addTag('routing.loader'); + $container->register('loader2')->addTag('routing.loader'); + + (new RoutingResolverPass())->process($container); + + $this->assertEquals( + array(array('addLoader', array(new Reference('loader1'))), array('addLoader', array(new Reference('loader2')))), + $container->getDefinition('routing.resolver')->getMethodCalls() + ); + } +} diff --git a/lib/silex/vendor/symfony/routing/Tests/Fixtures/AnnotatedClasses/AbstractClass.php b/lib/silex/vendor/symfony/routing/Tests/Fixtures/AnnotatedClasses/AbstractClass.php new file mode 100644 index 000000000..56bcab2a9 --- /dev/null +++ b/lib/silex/vendor/symfony/routing/Tests/Fixtures/AnnotatedClasses/AbstractClass.php @@ -0,0 +1,16 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Routing\Tests\Fixtures\AnnotatedClasses; + +abstract class AbstractClass +{ +} diff --git a/lib/silex/vendor/symfony/routing/Tests/Fixtures/AnnotatedClasses/BarClass.php b/lib/silex/vendor/symfony/routing/Tests/Fixtures/AnnotatedClasses/BarClass.php new file mode 100644 index 000000000..a3882773c --- /dev/null +++ b/lib/silex/vendor/symfony/routing/Tests/Fixtures/AnnotatedClasses/BarClass.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\Routing\Tests\Fixtures\AnnotatedClasses; + +class BarClass +{ + public function routeAction($arg1, $arg2 = 'defaultValue2', $arg3 = 'defaultValue3') + { + } +} diff --git a/lib/silex/vendor/symfony/routing/Tests/Fixtures/AnnotatedClasses/BazClass.php b/lib/silex/vendor/symfony/routing/Tests/Fixtures/AnnotatedClasses/BazClass.php new file mode 100644 index 000000000..471968b57 --- /dev/null +++ b/lib/silex/vendor/symfony/routing/Tests/Fixtures/AnnotatedClasses/BazClass.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\Routing\Tests\Fixtures\AnnotatedClasses; + +class BazClass +{ + public function __invoke() + { + } +} diff --git a/lib/silex/vendor/symfony/routing/Tests/Fixtures/AnnotatedClasses/FooClass.php b/lib/silex/vendor/symfony/routing/Tests/Fixtures/AnnotatedClasses/FooClass.php new file mode 100644 index 000000000..320dc3500 --- /dev/null +++ b/lib/silex/vendor/symfony/routing/Tests/Fixtures/AnnotatedClasses/FooClass.php @@ -0,0 +1,16 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Routing\Tests\Fixtures\AnnotatedClasses; + +class FooClass +{ +} diff --git a/lib/silex/vendor/symfony/routing/Tests/Fixtures/AnnotatedClasses/FooTrait.php b/lib/silex/vendor/symfony/routing/Tests/Fixtures/AnnotatedClasses/FooTrait.php new file mode 100644 index 000000000..ee8f4b071 --- /dev/null +++ b/lib/silex/vendor/symfony/routing/Tests/Fixtures/AnnotatedClasses/FooTrait.php @@ -0,0 +1,13 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Routing\Tests\Fixtures; + +use Symfony\Component\Routing\CompiledRoute; + +class CustomCompiledRoute extends CompiledRoute +{ +} diff --git a/lib/silex/vendor/symfony/routing/Tests/Fixtures/CustomRouteCompiler.php b/lib/silex/vendor/symfony/routing/Tests/Fixtures/CustomRouteCompiler.php new file mode 100644 index 000000000..c2e2afd9a --- /dev/null +++ b/lib/silex/vendor/symfony/routing/Tests/Fixtures/CustomRouteCompiler.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\Routing\Tests\Fixtures; + +use Symfony\Component\Routing\Route; +use Symfony\Component\Routing\RouteCompiler; + +class CustomRouteCompiler extends RouteCompiler +{ + /** + * {@inheritdoc} + */ + public static function compile(Route $route) + { + return new CustomCompiledRoute('', '', array(), array()); + } +} diff --git a/lib/silex/vendor/symfony/routing/Tests/Fixtures/CustomXmlFileLoader.php b/lib/silex/vendor/symfony/routing/Tests/Fixtures/CustomXmlFileLoader.php new file mode 100644 index 000000000..9fd5754a1 --- /dev/null +++ b/lib/silex/vendor/symfony/routing/Tests/Fixtures/CustomXmlFileLoader.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\Routing\Tests\Fixtures; + +use Symfony\Component\Routing\Loader\XmlFileLoader; +use Symfony\Component\Config\Util\XmlUtils; + +/** + * XmlFileLoader with schema validation turned off. + */ +class CustomXmlFileLoader extends XmlFileLoader +{ + protected function loadFile($file) + { + return XmlUtils::loadFile($file, function () { return true; }); + } +} diff --git a/lib/silex/vendor/symfony/routing/Tests/Fixtures/OtherAnnotatedClasses/AnonymousClassInTrait.php b/lib/silex/vendor/symfony/routing/Tests/Fixtures/OtherAnnotatedClasses/AnonymousClassInTrait.php new file mode 100644 index 000000000..de8789564 --- /dev/null +++ b/lib/silex/vendor/symfony/routing/Tests/Fixtures/OtherAnnotatedClasses/AnonymousClassInTrait.php @@ -0,0 +1,24 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Routing\Tests\Fixtures\OtherAnnotatedClasses; + +trait AnonymousClassInTrait +{ + public function test() + { + return new class() { + public function foo() + { + } + }; + } +} diff --git a/lib/silex/vendor/symfony/routing/Tests/Fixtures/OtherAnnotatedClasses/NoStartTagClass.php b/lib/silex/vendor/symfony/routing/Tests/Fixtures/OtherAnnotatedClasses/NoStartTagClass.php new file mode 100644 index 000000000..8900d34e5 --- /dev/null +++ b/lib/silex/vendor/symfony/routing/Tests/Fixtures/OtherAnnotatedClasses/NoStartTagClass.php @@ -0,0 +1,3 @@ +class NoStartTagClass +{ +} diff --git a/lib/silex/vendor/symfony/routing/Tests/Fixtures/OtherAnnotatedClasses/VariadicClass.php b/lib/silex/vendor/symfony/routing/Tests/Fixtures/OtherAnnotatedClasses/VariadicClass.php new file mode 100644 index 000000000..729c9b4d0 --- /dev/null +++ b/lib/silex/vendor/symfony/routing/Tests/Fixtures/OtherAnnotatedClasses/VariadicClass.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\Routing\Tests\Fixtures\OtherAnnotatedClasses; + +class VariadicClass +{ + public function routeAction(...$params) + { + } +} diff --git a/lib/silex/vendor/symfony/routing/Tests/Fixtures/RedirectableUrlMatcher.php b/lib/silex/vendor/symfony/routing/Tests/Fixtures/RedirectableUrlMatcher.php new file mode 100644 index 000000000..15937bcf0 --- /dev/null +++ b/lib/silex/vendor/symfony/routing/Tests/Fixtures/RedirectableUrlMatcher.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\Routing\Tests\Fixtures; + +use Symfony\Component\Routing\Matcher\UrlMatcher; +use Symfony\Component\Routing\Matcher\RedirectableUrlMatcherInterface; + +/** + * @author Fabien Potencier + */ +class RedirectableUrlMatcher extends UrlMatcher implements RedirectableUrlMatcherInterface +{ + public function redirect($path, $route, $scheme = null) + { + return array( + '_controller' => 'Some controller reference...', + 'path' => $path, + 'scheme' => $scheme, + ); + } +} diff --git a/lib/silex/vendor/symfony/routing/Tests/Fixtures/annotated.php b/lib/silex/vendor/symfony/routing/Tests/Fixtures/annotated.php new file mode 100644 index 000000000..e69de29bb diff --git a/lib/silex/vendor/symfony/routing/Tests/Fixtures/bad_format.yml b/lib/silex/vendor/symfony/routing/Tests/Fixtures/bad_format.yml new file mode 100644 index 000000000..8ba50e2e4 --- /dev/null +++ b/lib/silex/vendor/symfony/routing/Tests/Fixtures/bad_format.yml @@ -0,0 +1,3 @@ +blog_show: + path: /blog/{slug} + defaults: { _controller: "MyBundle:Blog:show" } diff --git a/lib/silex/vendor/symfony/routing/Tests/Fixtures/bar.xml b/lib/silex/vendor/symfony/routing/Tests/Fixtures/bar.xml new file mode 100644 index 000000000..e69de29bb diff --git a/lib/silex/vendor/symfony/routing/Tests/Fixtures/controller/import__controller.xml b/lib/silex/vendor/symfony/routing/Tests/Fixtures/controller/import__controller.xml new file mode 100644 index 000000000..bbe727d41 --- /dev/null +++ b/lib/silex/vendor/symfony/routing/Tests/Fixtures/controller/import__controller.xml @@ -0,0 +1,10 @@ + + + + + FrameworkBundle:Template:template + + diff --git a/lib/silex/vendor/symfony/routing/Tests/Fixtures/controller/import__controller.yml b/lib/silex/vendor/symfony/routing/Tests/Fixtures/controller/import__controller.yml new file mode 100644 index 000000000..4240b74dc --- /dev/null +++ b/lib/silex/vendor/symfony/routing/Tests/Fixtures/controller/import__controller.yml @@ -0,0 +1,4 @@ +_static: + resource: routing.yml + defaults: + _controller: FrameworkBundle:Template:template diff --git a/lib/silex/vendor/symfony/routing/Tests/Fixtures/controller/import_controller.xml b/lib/silex/vendor/symfony/routing/Tests/Fixtures/controller/import_controller.xml new file mode 100644 index 000000000..378b9ca18 --- /dev/null +++ b/lib/silex/vendor/symfony/routing/Tests/Fixtures/controller/import_controller.xml @@ -0,0 +1,8 @@ + + + + + diff --git a/lib/silex/vendor/symfony/routing/Tests/Fixtures/controller/import_controller.yml b/lib/silex/vendor/symfony/routing/Tests/Fixtures/controller/import_controller.yml new file mode 100644 index 000000000..1a71c62b2 --- /dev/null +++ b/lib/silex/vendor/symfony/routing/Tests/Fixtures/controller/import_controller.yml @@ -0,0 +1,3 @@ +_static: + resource: routing.yml + controller: FrameworkBundle:Template:template diff --git a/lib/silex/vendor/symfony/routing/Tests/Fixtures/controller/import_override_defaults.xml b/lib/silex/vendor/symfony/routing/Tests/Fixtures/controller/import_override_defaults.xml new file mode 100644 index 000000000..e3c755a40 --- /dev/null +++ b/lib/silex/vendor/symfony/routing/Tests/Fixtures/controller/import_override_defaults.xml @@ -0,0 +1,10 @@ + + + + + AppBundle:Blog:index + + diff --git a/lib/silex/vendor/symfony/routing/Tests/Fixtures/controller/import_override_defaults.yml b/lib/silex/vendor/symfony/routing/Tests/Fixtures/controller/import_override_defaults.yml new file mode 100644 index 000000000..db1ab3cbe --- /dev/null +++ b/lib/silex/vendor/symfony/routing/Tests/Fixtures/controller/import_override_defaults.yml @@ -0,0 +1,5 @@ +_static: + resource: routing.yml + controller: FrameworkBundle:Template:template + defaults: + _controller: AppBundle:Homepage:show diff --git a/lib/silex/vendor/symfony/routing/Tests/Fixtures/controller/override_defaults.xml b/lib/silex/vendor/symfony/routing/Tests/Fixtures/controller/override_defaults.xml new file mode 100644 index 000000000..f47c57b30 --- /dev/null +++ b/lib/silex/vendor/symfony/routing/Tests/Fixtures/controller/override_defaults.xml @@ -0,0 +1,10 @@ + + + + + AppBundle:Blog:index + + diff --git a/lib/silex/vendor/symfony/routing/Tests/Fixtures/controller/override_defaults.yml b/lib/silex/vendor/symfony/routing/Tests/Fixtures/controller/override_defaults.yml new file mode 100644 index 000000000..00a2c0e97 --- /dev/null +++ b/lib/silex/vendor/symfony/routing/Tests/Fixtures/controller/override_defaults.yml @@ -0,0 +1,5 @@ +app_blog: + path: /blog + controller: AppBundle:Homepage:show + defaults: + _controller: AppBundle:Blog:index diff --git a/lib/silex/vendor/symfony/routing/Tests/Fixtures/controller/routing.xml b/lib/silex/vendor/symfony/routing/Tests/Fixtures/controller/routing.xml new file mode 100644 index 000000000..6420138a6 --- /dev/null +++ b/lib/silex/vendor/symfony/routing/Tests/Fixtures/controller/routing.xml @@ -0,0 +1,14 @@ + + + + + + + AppBundle:Blog:list + + + + diff --git a/lib/silex/vendor/symfony/routing/Tests/Fixtures/controller/routing.yml b/lib/silex/vendor/symfony/routing/Tests/Fixtures/controller/routing.yml new file mode 100644 index 000000000..cb71ec3b7 --- /dev/null +++ b/lib/silex/vendor/symfony/routing/Tests/Fixtures/controller/routing.yml @@ -0,0 +1,11 @@ +app_homepage: + path: / + controller: AppBundle:Homepage:show + +app_blog: + path: /blog + defaults: + _controller: AppBundle:Blog:list + +app_logout: + path: /logout diff --git a/lib/silex/vendor/symfony/routing/Tests/Fixtures/directory/recurse/routes1.yml b/lib/silex/vendor/symfony/routing/Tests/Fixtures/directory/recurse/routes1.yml new file mode 100644 index 000000000..d07883662 --- /dev/null +++ b/lib/silex/vendor/symfony/routing/Tests/Fixtures/directory/recurse/routes1.yml @@ -0,0 +1,2 @@ +route1: + path: /route/1 diff --git a/lib/silex/vendor/symfony/routing/Tests/Fixtures/directory/recurse/routes2.yml b/lib/silex/vendor/symfony/routing/Tests/Fixtures/directory/recurse/routes2.yml new file mode 100644 index 000000000..938fb2457 --- /dev/null +++ b/lib/silex/vendor/symfony/routing/Tests/Fixtures/directory/recurse/routes2.yml @@ -0,0 +1,2 @@ +route2: + path: /route/2 diff --git a/lib/silex/vendor/symfony/routing/Tests/Fixtures/directory/routes3.yml b/lib/silex/vendor/symfony/routing/Tests/Fixtures/directory/routes3.yml new file mode 100644 index 000000000..088cfb4d4 --- /dev/null +++ b/lib/silex/vendor/symfony/routing/Tests/Fixtures/directory/routes3.yml @@ -0,0 +1,2 @@ +route3: + path: /route/3 diff --git a/lib/silex/vendor/symfony/routing/Tests/Fixtures/directory_import/import.yml b/lib/silex/vendor/symfony/routing/Tests/Fixtures/directory_import/import.yml new file mode 100644 index 000000000..af829e58c --- /dev/null +++ b/lib/silex/vendor/symfony/routing/Tests/Fixtures/directory_import/import.yml @@ -0,0 +1,3 @@ +_directory: + resource: "../directory" + type: directory diff --git a/lib/silex/vendor/symfony/routing/Tests/Fixtures/dumper/url_matcher0.php b/lib/silex/vendor/symfony/routing/Tests/Fixtures/dumper/url_matcher0.php new file mode 100644 index 000000000..839c7d137 --- /dev/null +++ b/lib/silex/vendor/symfony/routing/Tests/Fixtures/dumper/url_matcher0.php @@ -0,0 +1,39 @@ +context = $context; + } + + public function match($rawPathinfo) + { + $allow = array(); + $pathinfo = rawurldecode($rawPathinfo); + $trimmedPathinfo = rtrim($pathinfo, '/'); + $context = $this->context; + $request = $this->request; + $requestMethod = $canonicalMethod = $context->getMethod(); + $scheme = $context->getScheme(); + + if ('HEAD' === $requestMethod) { + $canonicalMethod = 'GET'; + } + + + if ('/' === $pathinfo) { + throw new Symfony\Component\Routing\Exception\NoConfigurationException(); + } + + throw 0 < count($allow) ? new MethodNotAllowedException(array_unique($allow)) : new ResourceNotFoundException(); + } +} diff --git a/lib/silex/vendor/symfony/routing/Tests/Fixtures/dumper/url_matcher1.php b/lib/silex/vendor/symfony/routing/Tests/Fixtures/dumper/url_matcher1.php new file mode 100644 index 000000000..5d7dca8e5 --- /dev/null +++ b/lib/silex/vendor/symfony/routing/Tests/Fixtures/dumper/url_matcher1.php @@ -0,0 +1,312 @@ +context = $context; + } + + public function match($rawPathinfo) + { + $allow = array(); + $pathinfo = rawurldecode($rawPathinfo); + $trimmedPathinfo = rtrim($pathinfo, '/'); + $context = $this->context; + $request = $this->request; + $requestMethod = $canonicalMethod = $context->getMethod(); + $scheme = $context->getScheme(); + + if ('HEAD' === $requestMethod) { + $canonicalMethod = 'GET'; + } + + + if (0 === strpos($pathinfo, '/foo')) { + // foo + if (preg_match('#^/foo/(?Pbaz|symfony)$#s', $pathinfo, $matches)) { + return $this->mergeDefaults(array_replace($matches, array('_route' => 'foo')), array ( 'def' => 'test',)); + } + + // foofoo + if ('/foofoo' === $pathinfo) { + return array ( 'def' => 'test', '_route' => 'foofoo',); + } + + } + + elseif (0 === strpos($pathinfo, '/bar')) { + // bar + if (preg_match('#^/bar/(?P[^/]++)$#s', $pathinfo, $matches)) { + if ('GET' !== $canonicalMethod) { + $allow[] = 'GET'; + goto not_bar; + } + + return $this->mergeDefaults(array_replace($matches, array('_route' => 'bar')), array ()); + } + not_bar: + + // barhead + if (0 === strpos($pathinfo, '/barhead') && preg_match('#^/barhead/(?P[^/]++)$#s', $pathinfo, $matches)) { + if ('GET' !== $canonicalMethod) { + $allow[] = 'GET'; + goto not_barhead; + } + + return $this->mergeDefaults(array_replace($matches, array('_route' => 'barhead')), array ()); + } + not_barhead: + + } + + elseif (0 === strpos($pathinfo, '/test')) { + if (0 === strpos($pathinfo, '/test/baz')) { + // baz + if ('/test/baz' === $pathinfo) { + return array('_route' => 'baz'); + } + + // baz2 + if ('/test/baz.html' === $pathinfo) { + return array('_route' => 'baz2'); + } + + // baz3 + if ('/test/baz3/' === $pathinfo) { + return array('_route' => 'baz3'); + } + + } + + // baz4 + if (preg_match('#^/test/(?P[^/]++)/$#s', $pathinfo, $matches)) { + return $this->mergeDefaults(array_replace($matches, array('_route' => 'baz4')), array ()); + } + + // baz5 + if (preg_match('#^/test/(?P[^/]++)/$#s', $pathinfo, $matches)) { + if ('POST' !== $canonicalMethod) { + $allow[] = 'POST'; + goto not_baz5; + } + + return $this->mergeDefaults(array_replace($matches, array('_route' => 'baz5')), array ()); + } + not_baz5: + + // baz.baz6 + if (preg_match('#^/test/(?P[^/]++)/$#s', $pathinfo, $matches)) { + if ('PUT' !== $canonicalMethod) { + $allow[] = 'PUT'; + goto not_bazbaz6; + } + + return $this->mergeDefaults(array_replace($matches, array('_route' => 'baz.baz6')), array ()); + } + not_bazbaz6: + + } + + // quoter + if (preg_match('#^/(?P[\']+)$#s', $pathinfo, $matches)) { + return $this->mergeDefaults(array_replace($matches, array('_route' => 'quoter')), array ()); + } + + // space + if ('/spa ce' === $pathinfo) { + return array('_route' => 'space'); + } + + if (0 === strpos($pathinfo, '/a')) { + if (0 === strpos($pathinfo, '/a/b\'b')) { + // foo1 + if (preg_match('#^/a/b\'b/(?P[^/]++)$#s', $pathinfo, $matches)) { + return $this->mergeDefaults(array_replace($matches, array('_route' => 'foo1')), array ()); + } + + // bar1 + if (preg_match('#^/a/b\'b/(?P[^/]++)$#s', $pathinfo, $matches)) { + return $this->mergeDefaults(array_replace($matches, array('_route' => 'bar1')), array ()); + } + + } + + // overridden + if (preg_match('#^/a/(?P.*)$#s', $pathinfo, $matches)) { + return $this->mergeDefaults(array_replace($matches, array('_route' => 'overridden')), array ()); + } + + if (0 === strpos($pathinfo, '/a/b\'b')) { + // foo2 + if (preg_match('#^/a/b\'b/(?P[^/]++)$#s', $pathinfo, $matches)) { + return $this->mergeDefaults(array_replace($matches, array('_route' => 'foo2')), array ()); + } + + // bar2 + if (preg_match('#^/a/b\'b/(?P[^/]++)$#s', $pathinfo, $matches)) { + return $this->mergeDefaults(array_replace($matches, array('_route' => 'bar2')), array ()); + } + + } + + } + + elseif (0 === strpos($pathinfo, '/multi')) { + // helloWorld + if (0 === strpos($pathinfo, '/multi/hello') && preg_match('#^/multi/hello(?:/(?P[^/]++))?$#s', $pathinfo, $matches)) { + return $this->mergeDefaults(array_replace($matches, array('_route' => 'helloWorld')), array ( 'who' => 'World!',)); + } + + // hey + if ('/multi/hey/' === $pathinfo) { + return array('_route' => 'hey'); + } + + // overridden2 + if ('/multi/new' === $pathinfo) { + return array('_route' => 'overridden2'); + } + + } + + // foo3 + if (preg_match('#^/(?P<_locale>[^/]++)/b/(?P[^/]++)$#s', $pathinfo, $matches)) { + return $this->mergeDefaults(array_replace($matches, array('_route' => 'foo3')), array ()); + } + + // bar3 + if (preg_match('#^/(?P<_locale>[^/]++)/b/(?P[^/]++)$#s', $pathinfo, $matches)) { + return $this->mergeDefaults(array_replace($matches, array('_route' => 'bar3')), array ()); + } + + if (0 === strpos($pathinfo, '/aba')) { + // ababa + if ('/ababa' === $pathinfo) { + return array('_route' => 'ababa'); + } + + // foo4 + if (preg_match('#^/aba/(?P[^/]++)$#s', $pathinfo, $matches)) { + return $this->mergeDefaults(array_replace($matches, array('_route' => 'foo4')), array ()); + } + + } + + $host = $context->getHost(); + + if (preg_match('#^a\\.example\\.com$#si', $host, $hostMatches)) { + // route1 + if ('/route1' === $pathinfo) { + return array('_route' => 'route1'); + } + + // route2 + if ('/c2/route2' === $pathinfo) { + return array('_route' => 'route2'); + } + + } + + if (preg_match('#^b\\.example\\.com$#si', $host, $hostMatches)) { + // route3 + if ('/c2/route3' === $pathinfo) { + return array('_route' => 'route3'); + } + + } + + if (preg_match('#^a\\.example\\.com$#si', $host, $hostMatches)) { + // route4 + if ('/route4' === $pathinfo) { + return array('_route' => 'route4'); + } + + } + + if (preg_match('#^c\\.example\\.com$#si', $host, $hostMatches)) { + // route5 + if ('/route5' === $pathinfo) { + return array('_route' => 'route5'); + } + + } + + // route6 + if ('/route6' === $pathinfo) { + return array('_route' => 'route6'); + } + + if (preg_match('#^(?P[^\\.]++)\\.example\\.com$#si', $host, $hostMatches)) { + if (0 === strpos($pathinfo, '/route1')) { + // route11 + if ('/route11' === $pathinfo) { + return $this->mergeDefaults(array_replace($hostMatches, array('_route' => 'route11')), array ()); + } + + // route12 + if ('/route12' === $pathinfo) { + return $this->mergeDefaults(array_replace($hostMatches, array('_route' => 'route12')), array ( 'var1' => 'val',)); + } + + // route13 + if (0 === strpos($pathinfo, '/route13') && preg_match('#^/route13/(?P[^/]++)$#s', $pathinfo, $matches)) { + return $this->mergeDefaults(array_replace($hostMatches, $matches, array('_route' => 'route13')), array ()); + } + + // route14 + if (0 === strpos($pathinfo, '/route14') && preg_match('#^/route14/(?P[^/]++)$#s', $pathinfo, $matches)) { + return $this->mergeDefaults(array_replace($hostMatches, $matches, array('_route' => 'route14')), array ( 'var1' => 'val',)); + } + + } + + } + + if (preg_match('#^c\\.example\\.com$#si', $host, $hostMatches)) { + // route15 + if (0 === strpos($pathinfo, '/route15') && preg_match('#^/route15/(?P[^/]++)$#s', $pathinfo, $matches)) { + return $this->mergeDefaults(array_replace($matches, array('_route' => 'route15')), array ()); + } + + } + + // route16 + if (0 === strpos($pathinfo, '/route16') && preg_match('#^/route16/(?P[^/]++)$#s', $pathinfo, $matches)) { + return $this->mergeDefaults(array_replace($matches, array('_route' => 'route16')), array ( 'var1' => 'val',)); + } + + // route17 + if ('/route17' === $pathinfo) { + return array('_route' => 'route17'); + } + + // a + if ('/a/a...' === $pathinfo) { + return array('_route' => 'a'); + } + + if (0 === strpos($pathinfo, '/a/b')) { + // b + if (preg_match('#^/a/b/(?P[^/]++)$#s', $pathinfo, $matches)) { + return $this->mergeDefaults(array_replace($matches, array('_route' => 'b')), array ()); + } + + // c + if (0 === strpos($pathinfo, '/a/b/c') && preg_match('#^/a/b/c/(?P[^/]++)$#s', $pathinfo, $matches)) { + return $this->mergeDefaults(array_replace($matches, array('_route' => 'c')), array ()); + } + + } + + throw 0 < count($allow) ? new MethodNotAllowedException(array_unique($allow)) : new ResourceNotFoundException(); + } +} diff --git a/lib/silex/vendor/symfony/routing/Tests/Fixtures/dumper/url_matcher2.php b/lib/silex/vendor/symfony/routing/Tests/Fixtures/dumper/url_matcher2.php new file mode 100644 index 000000000..49ce082a8 --- /dev/null +++ b/lib/silex/vendor/symfony/routing/Tests/Fixtures/dumper/url_matcher2.php @@ -0,0 +1,349 @@ +context = $context; + } + + public function match($rawPathinfo) + { + $allow = array(); + $pathinfo = rawurldecode($rawPathinfo); + $trimmedPathinfo = rtrim($pathinfo, '/'); + $context = $this->context; + $request = $this->request; + $requestMethod = $canonicalMethod = $context->getMethod(); + $scheme = $context->getScheme(); + + if ('HEAD' === $requestMethod) { + $canonicalMethod = 'GET'; + } + + + if (0 === strpos($pathinfo, '/foo')) { + // foo + if (preg_match('#^/foo/(?Pbaz|symfony)$#s', $pathinfo, $matches)) { + return $this->mergeDefaults(array_replace($matches, array('_route' => 'foo')), array ( 'def' => 'test',)); + } + + // foofoo + if ('/foofoo' === $pathinfo) { + return array ( 'def' => 'test', '_route' => 'foofoo',); + } + + } + + elseif (0 === strpos($pathinfo, '/bar')) { + // bar + if (preg_match('#^/bar/(?P[^/]++)$#s', $pathinfo, $matches)) { + if ('GET' !== $canonicalMethod) { + $allow[] = 'GET'; + goto not_bar; + } + + return $this->mergeDefaults(array_replace($matches, array('_route' => 'bar')), array ()); + } + not_bar: + + // barhead + if (0 === strpos($pathinfo, '/barhead') && preg_match('#^/barhead/(?P[^/]++)$#s', $pathinfo, $matches)) { + if ('GET' !== $canonicalMethod) { + $allow[] = 'GET'; + goto not_barhead; + } + + return $this->mergeDefaults(array_replace($matches, array('_route' => 'barhead')), array ()); + } + not_barhead: + + } + + elseif (0 === strpos($pathinfo, '/test')) { + if (0 === strpos($pathinfo, '/test/baz')) { + // baz + if ('/test/baz' === $pathinfo) { + return array('_route' => 'baz'); + } + + // baz2 + if ('/test/baz.html' === $pathinfo) { + return array('_route' => 'baz2'); + } + + // baz3 + if ('/test/baz3' === $trimmedPathinfo) { + $ret = array('_route' => 'baz3'); + if (substr($pathinfo, -1) !== '/') { + return array_replace($ret, $this->redirect($rawPathinfo.'/', 'baz3')); + } + + return $ret; + } + + } + + // baz4 + if (preg_match('#^/test/(?P[^/]++)/?$#s', $pathinfo, $matches)) { + $ret = $this->mergeDefaults(array_replace($matches, array('_route' => 'baz4')), array ()); + if (substr($pathinfo, -1) !== '/') { + return array_replace($ret, $this->redirect($rawPathinfo.'/', 'baz4')); + } + + return $ret; + } + + // baz5 + if (preg_match('#^/test/(?P[^/]++)/$#s', $pathinfo, $matches)) { + if ('POST' !== $canonicalMethod) { + $allow[] = 'POST'; + goto not_baz5; + } + + return $this->mergeDefaults(array_replace($matches, array('_route' => 'baz5')), array ()); + } + not_baz5: + + // baz.baz6 + if (preg_match('#^/test/(?P[^/]++)/$#s', $pathinfo, $matches)) { + if ('PUT' !== $canonicalMethod) { + $allow[] = 'PUT'; + goto not_bazbaz6; + } + + return $this->mergeDefaults(array_replace($matches, array('_route' => 'baz.baz6')), array ()); + } + not_bazbaz6: + + } + + // quoter + if (preg_match('#^/(?P[\']+)$#s', $pathinfo, $matches)) { + return $this->mergeDefaults(array_replace($matches, array('_route' => 'quoter')), array ()); + } + + // space + if ('/spa ce' === $pathinfo) { + return array('_route' => 'space'); + } + + if (0 === strpos($pathinfo, '/a')) { + if (0 === strpos($pathinfo, '/a/b\'b')) { + // foo1 + if (preg_match('#^/a/b\'b/(?P[^/]++)$#s', $pathinfo, $matches)) { + return $this->mergeDefaults(array_replace($matches, array('_route' => 'foo1')), array ()); + } + + // bar1 + if (preg_match('#^/a/b\'b/(?P[^/]++)$#s', $pathinfo, $matches)) { + return $this->mergeDefaults(array_replace($matches, array('_route' => 'bar1')), array ()); + } + + } + + // overridden + if (preg_match('#^/a/(?P.*)$#s', $pathinfo, $matches)) { + return $this->mergeDefaults(array_replace($matches, array('_route' => 'overridden')), array ()); + } + + if (0 === strpos($pathinfo, '/a/b\'b')) { + // foo2 + if (preg_match('#^/a/b\'b/(?P[^/]++)$#s', $pathinfo, $matches)) { + return $this->mergeDefaults(array_replace($matches, array('_route' => 'foo2')), array ()); + } + + // bar2 + if (preg_match('#^/a/b\'b/(?P[^/]++)$#s', $pathinfo, $matches)) { + return $this->mergeDefaults(array_replace($matches, array('_route' => 'bar2')), array ()); + } + + } + + } + + elseif (0 === strpos($pathinfo, '/multi')) { + // helloWorld + if (0 === strpos($pathinfo, '/multi/hello') && preg_match('#^/multi/hello(?:/(?P[^/]++))?$#s', $pathinfo, $matches)) { + return $this->mergeDefaults(array_replace($matches, array('_route' => 'helloWorld')), array ( 'who' => 'World!',)); + } + + // hey + if ('/multi/hey' === $trimmedPathinfo) { + $ret = array('_route' => 'hey'); + if (substr($pathinfo, -1) !== '/') { + return array_replace($ret, $this->redirect($rawPathinfo.'/', 'hey')); + } + + return $ret; + } + + // overridden2 + if ('/multi/new' === $pathinfo) { + return array('_route' => 'overridden2'); + } + + } + + // foo3 + if (preg_match('#^/(?P<_locale>[^/]++)/b/(?P[^/]++)$#s', $pathinfo, $matches)) { + return $this->mergeDefaults(array_replace($matches, array('_route' => 'foo3')), array ()); + } + + // bar3 + if (preg_match('#^/(?P<_locale>[^/]++)/b/(?P[^/]++)$#s', $pathinfo, $matches)) { + return $this->mergeDefaults(array_replace($matches, array('_route' => 'bar3')), array ()); + } + + if (0 === strpos($pathinfo, '/aba')) { + // ababa + if ('/ababa' === $pathinfo) { + return array('_route' => 'ababa'); + } + + // foo4 + if (preg_match('#^/aba/(?P[^/]++)$#s', $pathinfo, $matches)) { + return $this->mergeDefaults(array_replace($matches, array('_route' => 'foo4')), array ()); + } + + } + + $host = $context->getHost(); + + if (preg_match('#^a\\.example\\.com$#si', $host, $hostMatches)) { + // route1 + if ('/route1' === $pathinfo) { + return array('_route' => 'route1'); + } + + // route2 + if ('/c2/route2' === $pathinfo) { + return array('_route' => 'route2'); + } + + } + + if (preg_match('#^b\\.example\\.com$#si', $host, $hostMatches)) { + // route3 + if ('/c2/route3' === $pathinfo) { + return array('_route' => 'route3'); + } + + } + + if (preg_match('#^a\\.example\\.com$#si', $host, $hostMatches)) { + // route4 + if ('/route4' === $pathinfo) { + return array('_route' => 'route4'); + } + + } + + if (preg_match('#^c\\.example\\.com$#si', $host, $hostMatches)) { + // route5 + if ('/route5' === $pathinfo) { + return array('_route' => 'route5'); + } + + } + + // route6 + if ('/route6' === $pathinfo) { + return array('_route' => 'route6'); + } + + if (preg_match('#^(?P[^\\.]++)\\.example\\.com$#si', $host, $hostMatches)) { + if (0 === strpos($pathinfo, '/route1')) { + // route11 + if ('/route11' === $pathinfo) { + return $this->mergeDefaults(array_replace($hostMatches, array('_route' => 'route11')), array ()); + } + + // route12 + if ('/route12' === $pathinfo) { + return $this->mergeDefaults(array_replace($hostMatches, array('_route' => 'route12')), array ( 'var1' => 'val',)); + } + + // route13 + if (0 === strpos($pathinfo, '/route13') && preg_match('#^/route13/(?P[^/]++)$#s', $pathinfo, $matches)) { + return $this->mergeDefaults(array_replace($hostMatches, $matches, array('_route' => 'route13')), array ()); + } + + // route14 + if (0 === strpos($pathinfo, '/route14') && preg_match('#^/route14/(?P[^/]++)$#s', $pathinfo, $matches)) { + return $this->mergeDefaults(array_replace($hostMatches, $matches, array('_route' => 'route14')), array ( 'var1' => 'val',)); + } + + } + + } + + if (preg_match('#^c\\.example\\.com$#si', $host, $hostMatches)) { + // route15 + if (0 === strpos($pathinfo, '/route15') && preg_match('#^/route15/(?P[^/]++)$#s', $pathinfo, $matches)) { + return $this->mergeDefaults(array_replace($matches, array('_route' => 'route15')), array ()); + } + + } + + // route16 + if (0 === strpos($pathinfo, '/route16') && preg_match('#^/route16/(?P[^/]++)$#s', $pathinfo, $matches)) { + return $this->mergeDefaults(array_replace($matches, array('_route' => 'route16')), array ( 'var1' => 'val',)); + } + + // route17 + if ('/route17' === $pathinfo) { + return array('_route' => 'route17'); + } + + // a + if ('/a/a...' === $pathinfo) { + return array('_route' => 'a'); + } + + if (0 === strpos($pathinfo, '/a/b')) { + // b + if (preg_match('#^/a/b/(?P[^/]++)$#s', $pathinfo, $matches)) { + return $this->mergeDefaults(array_replace($matches, array('_route' => 'b')), array ()); + } + + // c + if (0 === strpos($pathinfo, '/a/b/c') && preg_match('#^/a/b/c/(?P[^/]++)$#s', $pathinfo, $matches)) { + return $this->mergeDefaults(array_replace($matches, array('_route' => 'c')), array ()); + } + + } + + // secure + if ('/secure' === $pathinfo) { + $ret = array('_route' => 'secure'); + $requiredSchemes = array ( 'https' => 0,); + if (!isset($requiredSchemes[$scheme])) { + return array_replace($ret, $this->redirect($rawPathinfo, 'secure', key($requiredSchemes))); + } + + return $ret; + } + + // nonsecure + if ('/nonsecure' === $pathinfo) { + $ret = array('_route' => 'nonsecure'); + $requiredSchemes = array ( 'http' => 0,); + if (!isset($requiredSchemes[$scheme])) { + return array_replace($ret, $this->redirect($rawPathinfo, 'nonsecure', key($requiredSchemes))); + } + + return $ret; + } + + throw 0 < count($allow) ? new MethodNotAllowedException(array_unique($allow)) : new ResourceNotFoundException(); + } +} diff --git a/lib/silex/vendor/symfony/routing/Tests/Fixtures/dumper/url_matcher3.php b/lib/silex/vendor/symfony/routing/Tests/Fixtures/dumper/url_matcher3.php new file mode 100644 index 000000000..ae5495631 --- /dev/null +++ b/lib/silex/vendor/symfony/routing/Tests/Fixtures/dumper/url_matcher3.php @@ -0,0 +1,53 @@ +context = $context; + } + + public function match($rawPathinfo) + { + $allow = array(); + $pathinfo = rawurldecode($rawPathinfo); + $trimmedPathinfo = rtrim($pathinfo, '/'); + $context = $this->context; + $request = $this->request; + $requestMethod = $canonicalMethod = $context->getMethod(); + $scheme = $context->getScheme(); + + if ('HEAD' === $requestMethod) { + $canonicalMethod = 'GET'; + } + + + if (0 === strpos($pathinfo, '/rootprefix')) { + // static + if ('/rootprefix/test' === $pathinfo) { + return array('_route' => 'static'); + } + + // dynamic + if (preg_match('#^/rootprefix/(?P[^/]++)$#s', $pathinfo, $matches)) { + return $this->mergeDefaults(array_replace($matches, array('_route' => 'dynamic')), array ()); + } + + } + + // with-condition + if ('/with-condition' === $pathinfo && ($context->getMethod() == "GET")) { + return array('_route' => 'with-condition'); + } + + throw 0 < count($allow) ? new MethodNotAllowedException(array_unique($allow)) : new ResourceNotFoundException(); + } +} diff --git a/lib/silex/vendor/symfony/routing/Tests/Fixtures/dumper/url_matcher4.php b/lib/silex/vendor/symfony/routing/Tests/Fixtures/dumper/url_matcher4.php new file mode 100644 index 000000000..50da489fb --- /dev/null +++ b/lib/silex/vendor/symfony/routing/Tests/Fixtures/dumper/url_matcher4.php @@ -0,0 +1,104 @@ +context = $context; + } + + public function match($rawPathinfo) + { + $allow = array(); + $pathinfo = rawurldecode($rawPathinfo); + $trimmedPathinfo = rtrim($pathinfo, '/'); + $context = $this->context; + $request = $this->request; + $requestMethod = $canonicalMethod = $context->getMethod(); + $scheme = $context->getScheme(); + + if ('HEAD' === $requestMethod) { + $canonicalMethod = 'GET'; + } + + + // just_head + if ('/just_head' === $pathinfo) { + if ('HEAD' !== $requestMethod) { + $allow[] = 'HEAD'; + goto not_just_head; + } + + return array('_route' => 'just_head'); + } + not_just_head: + + // head_and_get + if ('/head_and_get' === $pathinfo) { + if ('GET' !== $canonicalMethod) { + $allow[] = 'GET'; + goto not_head_and_get; + } + + return array('_route' => 'head_and_get'); + } + not_head_and_get: + + // get_and_head + if ('/get_and_head' === $pathinfo) { + if ('GET' !== $canonicalMethod) { + $allow[] = 'GET'; + goto not_get_and_head; + } + + return array('_route' => 'get_and_head'); + } + not_get_and_head: + + // post_and_head + if ('/post_and_get' === $pathinfo) { + if (!in_array($requestMethod, array('POST', 'HEAD'))) { + $allow = array_merge($allow, array('POST', 'HEAD')); + goto not_post_and_head; + } + + return array('_route' => 'post_and_head'); + } + not_post_and_head: + + if (0 === strpos($pathinfo, '/put_and_post')) { + // put_and_post + if ('/put_and_post' === $pathinfo) { + if (!in_array($requestMethod, array('PUT', 'POST'))) { + $allow = array_merge($allow, array('PUT', 'POST')); + goto not_put_and_post; + } + + return array('_route' => 'put_and_post'); + } + not_put_and_post: + + // put_and_get_and_head + if ('/put_and_post' === $pathinfo) { + if (!in_array($canonicalMethod, array('PUT', 'GET'))) { + $allow = array_merge($allow, array('PUT', 'GET')); + goto not_put_and_get_and_head; + } + + return array('_route' => 'put_and_get_and_head'); + } + not_put_and_get_and_head: + + } + + throw 0 < count($allow) ? new MethodNotAllowedException(array_unique($allow)) : new ResourceNotFoundException(); + } +} diff --git a/lib/silex/vendor/symfony/routing/Tests/Fixtures/dumper/url_matcher5.php b/lib/silex/vendor/symfony/routing/Tests/Fixtures/dumper/url_matcher5.php new file mode 100644 index 000000000..51be5b0bc --- /dev/null +++ b/lib/silex/vendor/symfony/routing/Tests/Fixtures/dumper/url_matcher5.php @@ -0,0 +1,162 @@ +context = $context; + } + + public function match($rawPathinfo) + { + $allow = array(); + $pathinfo = rawurldecode($rawPathinfo); + $trimmedPathinfo = rtrim($pathinfo, '/'); + $context = $this->context; + $request = $this->request; + $requestMethod = $canonicalMethod = $context->getMethod(); + $scheme = $context->getScheme(); + + if ('HEAD' === $requestMethod) { + $canonicalMethod = 'GET'; + } + + + if (0 === strpos($pathinfo, '/a')) { + // a_first + if ('/a/11' === $pathinfo) { + return array('_route' => 'a_first'); + } + + // a_second + if ('/a/22' === $pathinfo) { + return array('_route' => 'a_second'); + } + + // a_third + if ('/a/333' === $pathinfo) { + return array('_route' => 'a_third'); + } + + } + + // a_wildcard + if (preg_match('#^/(?P[^/]++)$#s', $pathinfo, $matches)) { + return $this->mergeDefaults(array_replace($matches, array('_route' => 'a_wildcard')), array ()); + } + + if (0 === strpos($pathinfo, '/a')) { + // a_fourth + if ('/a/44' === $trimmedPathinfo) { + $ret = array('_route' => 'a_fourth'); + if (substr($pathinfo, -1) !== '/') { + return array_replace($ret, $this->redirect($rawPathinfo.'/', 'a_fourth')); + } + + return $ret; + } + + // a_fifth + if ('/a/55' === $trimmedPathinfo) { + $ret = array('_route' => 'a_fifth'); + if (substr($pathinfo, -1) !== '/') { + return array_replace($ret, $this->redirect($rawPathinfo.'/', 'a_fifth')); + } + + return $ret; + } + + // a_sixth + if ('/a/66' === $trimmedPathinfo) { + $ret = array('_route' => 'a_sixth'); + if (substr($pathinfo, -1) !== '/') { + return array_replace($ret, $this->redirect($rawPathinfo.'/', 'a_sixth')); + } + + return $ret; + } + + } + + // nested_wildcard + if (0 === strpos($pathinfo, '/nested') && preg_match('#^/nested/(?P[^/]++)$#s', $pathinfo, $matches)) { + return $this->mergeDefaults(array_replace($matches, array('_route' => 'nested_wildcard')), array ()); + } + + if (0 === strpos($pathinfo, '/nested/group')) { + // nested_a + if ('/nested/group/a' === $trimmedPathinfo) { + $ret = array('_route' => 'nested_a'); + if (substr($pathinfo, -1) !== '/') { + return array_replace($ret, $this->redirect($rawPathinfo.'/', 'nested_a')); + } + + return $ret; + } + + // nested_b + if ('/nested/group/b' === $trimmedPathinfo) { + $ret = array('_route' => 'nested_b'); + if (substr($pathinfo, -1) !== '/') { + return array_replace($ret, $this->redirect($rawPathinfo.'/', 'nested_b')); + } + + return $ret; + } + + // nested_c + if ('/nested/group/c' === $trimmedPathinfo) { + $ret = array('_route' => 'nested_c'); + if (substr($pathinfo, -1) !== '/') { + return array_replace($ret, $this->redirect($rawPathinfo.'/', 'nested_c')); + } + + return $ret; + } + + } + + elseif (0 === strpos($pathinfo, '/slashed/group')) { + // slashed_a + if ('/slashed/group' === $trimmedPathinfo) { + $ret = array('_route' => 'slashed_a'); + if (substr($pathinfo, -1) !== '/') { + return array_replace($ret, $this->redirect($rawPathinfo.'/', 'slashed_a')); + } + + return $ret; + } + + // slashed_b + if ('/slashed/group/b' === $trimmedPathinfo) { + $ret = array('_route' => 'slashed_b'); + if (substr($pathinfo, -1) !== '/') { + return array_replace($ret, $this->redirect($rawPathinfo.'/', 'slashed_b')); + } + + return $ret; + } + + // slashed_c + if ('/slashed/group/c' === $trimmedPathinfo) { + $ret = array('_route' => 'slashed_c'); + if (substr($pathinfo, -1) !== '/') { + return array_replace($ret, $this->redirect($rawPathinfo.'/', 'slashed_c')); + } + + return $ret; + } + + } + + throw 0 < count($allow) ? new MethodNotAllowedException(array_unique($allow)) : new ResourceNotFoundException(); + } +} diff --git a/lib/silex/vendor/symfony/routing/Tests/Fixtures/dumper/url_matcher6.php b/lib/silex/vendor/symfony/routing/Tests/Fixtures/dumper/url_matcher6.php new file mode 100644 index 000000000..933525699 --- /dev/null +++ b/lib/silex/vendor/symfony/routing/Tests/Fixtures/dumper/url_matcher6.php @@ -0,0 +1,199 @@ +context = $context; + } + + public function match($rawPathinfo) + { + $allow = array(); + $pathinfo = rawurldecode($rawPathinfo); + $trimmedPathinfo = rtrim($pathinfo, '/'); + $context = $this->context; + $request = $this->request; + $requestMethod = $canonicalMethod = $context->getMethod(); + $scheme = $context->getScheme(); + + if ('HEAD' === $requestMethod) { + $canonicalMethod = 'GET'; + } + + + if (0 === strpos($pathinfo, '/trailing/simple')) { + // simple_trailing_slash_no_methods + if ('/trailing/simple/no-methods/' === $pathinfo) { + return array('_route' => 'simple_trailing_slash_no_methods'); + } + + // simple_trailing_slash_GET_method + if ('/trailing/simple/get-method/' === $pathinfo) { + if ('GET' !== $canonicalMethod) { + $allow[] = 'GET'; + goto not_simple_trailing_slash_GET_method; + } + + return array('_route' => 'simple_trailing_slash_GET_method'); + } + not_simple_trailing_slash_GET_method: + + // simple_trailing_slash_HEAD_method + if ('/trailing/simple/head-method/' === $pathinfo) { + if ('HEAD' !== $requestMethod) { + $allow[] = 'HEAD'; + goto not_simple_trailing_slash_HEAD_method; + } + + return array('_route' => 'simple_trailing_slash_HEAD_method'); + } + not_simple_trailing_slash_HEAD_method: + + // simple_trailing_slash_POST_method + if ('/trailing/simple/post-method/' === $pathinfo) { + if ('POST' !== $canonicalMethod) { + $allow[] = 'POST'; + goto not_simple_trailing_slash_POST_method; + } + + return array('_route' => 'simple_trailing_slash_POST_method'); + } + not_simple_trailing_slash_POST_method: + + } + + elseif (0 === strpos($pathinfo, '/trailing/regex')) { + // regex_trailing_slash_no_methods + if (0 === strpos($pathinfo, '/trailing/regex/no-methods') && preg_match('#^/trailing/regex/no\\-methods/(?P[^/]++)/$#s', $pathinfo, $matches)) { + return $this->mergeDefaults(array_replace($matches, array('_route' => 'regex_trailing_slash_no_methods')), array ()); + } + + // regex_trailing_slash_GET_method + if (0 === strpos($pathinfo, '/trailing/regex/get-method') && preg_match('#^/trailing/regex/get\\-method/(?P[^/]++)/$#s', $pathinfo, $matches)) { + if ('GET' !== $canonicalMethod) { + $allow[] = 'GET'; + goto not_regex_trailing_slash_GET_method; + } + + return $this->mergeDefaults(array_replace($matches, array('_route' => 'regex_trailing_slash_GET_method')), array ()); + } + not_regex_trailing_slash_GET_method: + + // regex_trailing_slash_HEAD_method + if (0 === strpos($pathinfo, '/trailing/regex/head-method') && preg_match('#^/trailing/regex/head\\-method/(?P[^/]++)/$#s', $pathinfo, $matches)) { + if ('HEAD' !== $requestMethod) { + $allow[] = 'HEAD'; + goto not_regex_trailing_slash_HEAD_method; + } + + return $this->mergeDefaults(array_replace($matches, array('_route' => 'regex_trailing_slash_HEAD_method')), array ()); + } + not_regex_trailing_slash_HEAD_method: + + // regex_trailing_slash_POST_method + if (0 === strpos($pathinfo, '/trailing/regex/post-method') && preg_match('#^/trailing/regex/post\\-method/(?P[^/]++)/$#s', $pathinfo, $matches)) { + if ('POST' !== $canonicalMethod) { + $allow[] = 'POST'; + goto not_regex_trailing_slash_POST_method; + } + + return $this->mergeDefaults(array_replace($matches, array('_route' => 'regex_trailing_slash_POST_method')), array ()); + } + not_regex_trailing_slash_POST_method: + + } + + elseif (0 === strpos($pathinfo, '/not-trailing/simple')) { + // simple_not_trailing_slash_no_methods + if ('/not-trailing/simple/no-methods' === $pathinfo) { + return array('_route' => 'simple_not_trailing_slash_no_methods'); + } + + // simple_not_trailing_slash_GET_method + if ('/not-trailing/simple/get-method' === $pathinfo) { + if ('GET' !== $canonicalMethod) { + $allow[] = 'GET'; + goto not_simple_not_trailing_slash_GET_method; + } + + return array('_route' => 'simple_not_trailing_slash_GET_method'); + } + not_simple_not_trailing_slash_GET_method: + + // simple_not_trailing_slash_HEAD_method + if ('/not-trailing/simple/head-method' === $pathinfo) { + if ('HEAD' !== $requestMethod) { + $allow[] = 'HEAD'; + goto not_simple_not_trailing_slash_HEAD_method; + } + + return array('_route' => 'simple_not_trailing_slash_HEAD_method'); + } + not_simple_not_trailing_slash_HEAD_method: + + // simple_not_trailing_slash_POST_method + if ('/not-trailing/simple/post-method' === $pathinfo) { + if ('POST' !== $canonicalMethod) { + $allow[] = 'POST'; + goto not_simple_not_trailing_slash_POST_method; + } + + return array('_route' => 'simple_not_trailing_slash_POST_method'); + } + not_simple_not_trailing_slash_POST_method: + + } + + elseif (0 === strpos($pathinfo, '/not-trailing/regex')) { + // regex_not_trailing_slash_no_methods + if (0 === strpos($pathinfo, '/not-trailing/regex/no-methods') && preg_match('#^/not\\-trailing/regex/no\\-methods/(?P[^/]++)$#s', $pathinfo, $matches)) { + return $this->mergeDefaults(array_replace($matches, array('_route' => 'regex_not_trailing_slash_no_methods')), array ()); + } + + // regex_not_trailing_slash_GET_method + if (0 === strpos($pathinfo, '/not-trailing/regex/get-method') && preg_match('#^/not\\-trailing/regex/get\\-method/(?P[^/]++)$#s', $pathinfo, $matches)) { + if ('GET' !== $canonicalMethod) { + $allow[] = 'GET'; + goto not_regex_not_trailing_slash_GET_method; + } + + return $this->mergeDefaults(array_replace($matches, array('_route' => 'regex_not_trailing_slash_GET_method')), array ()); + } + not_regex_not_trailing_slash_GET_method: + + // regex_not_trailing_slash_HEAD_method + if (0 === strpos($pathinfo, '/not-trailing/regex/head-method') && preg_match('#^/not\\-trailing/regex/head\\-method/(?P[^/]++)$#s', $pathinfo, $matches)) { + if ('HEAD' !== $requestMethod) { + $allow[] = 'HEAD'; + goto not_regex_not_trailing_slash_HEAD_method; + } + + return $this->mergeDefaults(array_replace($matches, array('_route' => 'regex_not_trailing_slash_HEAD_method')), array ()); + } + not_regex_not_trailing_slash_HEAD_method: + + // regex_not_trailing_slash_POST_method + if (0 === strpos($pathinfo, '/not-trailing/regex/post-method') && preg_match('#^/not\\-trailing/regex/post\\-method/(?P[^/]++)$#s', $pathinfo, $matches)) { + if ('POST' !== $canonicalMethod) { + $allow[] = 'POST'; + goto not_regex_not_trailing_slash_POST_method; + } + + return $this->mergeDefaults(array_replace($matches, array('_route' => 'regex_not_trailing_slash_POST_method')), array ()); + } + not_regex_not_trailing_slash_POST_method: + + } + + throw 0 < count($allow) ? new MethodNotAllowedException(array_unique($allow)) : new ResourceNotFoundException(); + } +} diff --git a/lib/silex/vendor/symfony/routing/Tests/Fixtures/dumper/url_matcher7.php b/lib/silex/vendor/symfony/routing/Tests/Fixtures/dumper/url_matcher7.php new file mode 100644 index 000000000..bceee6f3a --- /dev/null +++ b/lib/silex/vendor/symfony/routing/Tests/Fixtures/dumper/url_matcher7.php @@ -0,0 +1,229 @@ +context = $context; + } + + public function match($rawPathinfo) + { + $allow = array(); + $pathinfo = rawurldecode($rawPathinfo); + $trimmedPathinfo = rtrim($pathinfo, '/'); + $context = $this->context; + $request = $this->request; + $requestMethod = $canonicalMethod = $context->getMethod(); + $scheme = $context->getScheme(); + + if ('HEAD' === $requestMethod) { + $canonicalMethod = 'GET'; + } + + + if (0 === strpos($pathinfo, '/trailing/simple')) { + // simple_trailing_slash_no_methods + if ('/trailing/simple/no-methods' === $trimmedPathinfo) { + $ret = array('_route' => 'simple_trailing_slash_no_methods'); + if (substr($pathinfo, -1) !== '/') { + return array_replace($ret, $this->redirect($rawPathinfo.'/', 'simple_trailing_slash_no_methods')); + } + + return $ret; + } + + // simple_trailing_slash_GET_method + if ('/trailing/simple/get-method' === $trimmedPathinfo) { + if ('GET' !== $canonicalMethod) { + $allow[] = 'GET'; + goto not_simple_trailing_slash_GET_method; + } + + $ret = array('_route' => 'simple_trailing_slash_GET_method'); + if (substr($pathinfo, -1) !== '/') { + return array_replace($ret, $this->redirect($rawPathinfo.'/', 'simple_trailing_slash_GET_method')); + } + + return $ret; + } + not_simple_trailing_slash_GET_method: + + // simple_trailing_slash_HEAD_method + if ('/trailing/simple/head-method' === $trimmedPathinfo) { + if ('HEAD' !== $requestMethod) { + $allow[] = 'HEAD'; + goto not_simple_trailing_slash_HEAD_method; + } + + $ret = array('_route' => 'simple_trailing_slash_HEAD_method'); + if (substr($pathinfo, -1) !== '/') { + return array_replace($ret, $this->redirect($rawPathinfo.'/', 'simple_trailing_slash_HEAD_method')); + } + + return $ret; + } + not_simple_trailing_slash_HEAD_method: + + // simple_trailing_slash_POST_method + if ('/trailing/simple/post-method/' === $pathinfo) { + if ('POST' !== $canonicalMethod) { + $allow[] = 'POST'; + goto not_simple_trailing_slash_POST_method; + } + + return array('_route' => 'simple_trailing_slash_POST_method'); + } + not_simple_trailing_slash_POST_method: + + } + + elseif (0 === strpos($pathinfo, '/trailing/regex')) { + // regex_trailing_slash_no_methods + if (0 === strpos($pathinfo, '/trailing/regex/no-methods') && preg_match('#^/trailing/regex/no\\-methods/(?P[^/]++)/?$#s', $pathinfo, $matches)) { + $ret = $this->mergeDefaults(array_replace($matches, array('_route' => 'regex_trailing_slash_no_methods')), array ()); + if (substr($pathinfo, -1) !== '/') { + return array_replace($ret, $this->redirect($rawPathinfo.'/', 'regex_trailing_slash_no_methods')); + } + + return $ret; + } + + // regex_trailing_slash_GET_method + if (0 === strpos($pathinfo, '/trailing/regex/get-method') && preg_match('#^/trailing/regex/get\\-method/(?P[^/]++)/?$#s', $pathinfo, $matches)) { + if ('GET' !== $canonicalMethod) { + $allow[] = 'GET'; + goto not_regex_trailing_slash_GET_method; + } + + $ret = $this->mergeDefaults(array_replace($matches, array('_route' => 'regex_trailing_slash_GET_method')), array ()); + if (substr($pathinfo, -1) !== '/') { + return array_replace($ret, $this->redirect($rawPathinfo.'/', 'regex_trailing_slash_GET_method')); + } + + return $ret; + } + not_regex_trailing_slash_GET_method: + + // regex_trailing_slash_HEAD_method + if (0 === strpos($pathinfo, '/trailing/regex/head-method') && preg_match('#^/trailing/regex/head\\-method/(?P[^/]++)/?$#s', $pathinfo, $matches)) { + if ('HEAD' !== $requestMethod) { + $allow[] = 'HEAD'; + goto not_regex_trailing_slash_HEAD_method; + } + + $ret = $this->mergeDefaults(array_replace($matches, array('_route' => 'regex_trailing_slash_HEAD_method')), array ()); + if (substr($pathinfo, -1) !== '/') { + return array_replace($ret, $this->redirect($rawPathinfo.'/', 'regex_trailing_slash_HEAD_method')); + } + + return $ret; + } + not_regex_trailing_slash_HEAD_method: + + // regex_trailing_slash_POST_method + if (0 === strpos($pathinfo, '/trailing/regex/post-method') && preg_match('#^/trailing/regex/post\\-method/(?P[^/]++)/$#s', $pathinfo, $matches)) { + if ('POST' !== $canonicalMethod) { + $allow[] = 'POST'; + goto not_regex_trailing_slash_POST_method; + } + + return $this->mergeDefaults(array_replace($matches, array('_route' => 'regex_trailing_slash_POST_method')), array ()); + } + not_regex_trailing_slash_POST_method: + + } + + elseif (0 === strpos($pathinfo, '/not-trailing/simple')) { + // simple_not_trailing_slash_no_methods + if ('/not-trailing/simple/no-methods' === $pathinfo) { + return array('_route' => 'simple_not_trailing_slash_no_methods'); + } + + // simple_not_trailing_slash_GET_method + if ('/not-trailing/simple/get-method' === $pathinfo) { + if ('GET' !== $canonicalMethod) { + $allow[] = 'GET'; + goto not_simple_not_trailing_slash_GET_method; + } + + return array('_route' => 'simple_not_trailing_slash_GET_method'); + } + not_simple_not_trailing_slash_GET_method: + + // simple_not_trailing_slash_HEAD_method + if ('/not-trailing/simple/head-method' === $pathinfo) { + if ('HEAD' !== $requestMethod) { + $allow[] = 'HEAD'; + goto not_simple_not_trailing_slash_HEAD_method; + } + + return array('_route' => 'simple_not_trailing_slash_HEAD_method'); + } + not_simple_not_trailing_slash_HEAD_method: + + // simple_not_trailing_slash_POST_method + if ('/not-trailing/simple/post-method' === $pathinfo) { + if ('POST' !== $canonicalMethod) { + $allow[] = 'POST'; + goto not_simple_not_trailing_slash_POST_method; + } + + return array('_route' => 'simple_not_trailing_slash_POST_method'); + } + not_simple_not_trailing_slash_POST_method: + + } + + elseif (0 === strpos($pathinfo, '/not-trailing/regex')) { + // regex_not_trailing_slash_no_methods + if (0 === strpos($pathinfo, '/not-trailing/regex/no-methods') && preg_match('#^/not\\-trailing/regex/no\\-methods/(?P[^/]++)$#s', $pathinfo, $matches)) { + return $this->mergeDefaults(array_replace($matches, array('_route' => 'regex_not_trailing_slash_no_methods')), array ()); + } + + // regex_not_trailing_slash_GET_method + if (0 === strpos($pathinfo, '/not-trailing/regex/get-method') && preg_match('#^/not\\-trailing/regex/get\\-method/(?P[^/]++)$#s', $pathinfo, $matches)) { + if ('GET' !== $canonicalMethod) { + $allow[] = 'GET'; + goto not_regex_not_trailing_slash_GET_method; + } + + return $this->mergeDefaults(array_replace($matches, array('_route' => 'regex_not_trailing_slash_GET_method')), array ()); + } + not_regex_not_trailing_slash_GET_method: + + // regex_not_trailing_slash_HEAD_method + if (0 === strpos($pathinfo, '/not-trailing/regex/head-method') && preg_match('#^/not\\-trailing/regex/head\\-method/(?P[^/]++)$#s', $pathinfo, $matches)) { + if ('HEAD' !== $requestMethod) { + $allow[] = 'HEAD'; + goto not_regex_not_trailing_slash_HEAD_method; + } + + return $this->mergeDefaults(array_replace($matches, array('_route' => 'regex_not_trailing_slash_HEAD_method')), array ()); + } + not_regex_not_trailing_slash_HEAD_method: + + // regex_not_trailing_slash_POST_method + if (0 === strpos($pathinfo, '/not-trailing/regex/post-method') && preg_match('#^/not\\-trailing/regex/post\\-method/(?P[^/]++)$#s', $pathinfo, $matches)) { + if ('POST' !== $canonicalMethod) { + $allow[] = 'POST'; + goto not_regex_not_trailing_slash_POST_method; + } + + return $this->mergeDefaults(array_replace($matches, array('_route' => 'regex_not_trailing_slash_POST_method')), array ()); + } + not_regex_not_trailing_slash_POST_method: + + } + + throw 0 < count($allow) ? new MethodNotAllowedException(array_unique($allow)) : new ResourceNotFoundException(); + } +} diff --git a/lib/silex/vendor/symfony/routing/Tests/Fixtures/empty.yml b/lib/silex/vendor/symfony/routing/Tests/Fixtures/empty.yml new file mode 100644 index 000000000..e69de29bb diff --git a/lib/silex/vendor/symfony/routing/Tests/Fixtures/file_resource.yml b/lib/silex/vendor/symfony/routing/Tests/Fixtures/file_resource.yml new file mode 100644 index 000000000..e69de29bb diff --git a/lib/silex/vendor/symfony/routing/Tests/Fixtures/foo.xml b/lib/silex/vendor/symfony/routing/Tests/Fixtures/foo.xml new file mode 100644 index 000000000..e69de29bb diff --git a/lib/silex/vendor/symfony/routing/Tests/Fixtures/foo1.xml b/lib/silex/vendor/symfony/routing/Tests/Fixtures/foo1.xml new file mode 100644 index 000000000..e69de29bb diff --git a/lib/silex/vendor/symfony/routing/Tests/Fixtures/incomplete.yml b/lib/silex/vendor/symfony/routing/Tests/Fixtures/incomplete.yml new file mode 100644 index 000000000..df64d324f --- /dev/null +++ b/lib/silex/vendor/symfony/routing/Tests/Fixtures/incomplete.yml @@ -0,0 +1,2 @@ +blog_show: + defaults: { _controller: MyBlogBundle:Blog:show } diff --git a/lib/silex/vendor/symfony/routing/Tests/Fixtures/list_defaults.xml b/lib/silex/vendor/symfony/routing/Tests/Fixtures/list_defaults.xml new file mode 100644 index 000000000..f93bf9c6a --- /dev/null +++ b/lib/silex/vendor/symfony/routing/Tests/Fixtures/list_defaults.xml @@ -0,0 +1,20 @@ + + + + + + AcmeBlogBundle:Blog:index + + + + true + 1 + 3.5 + foo + + + + diff --git a/lib/silex/vendor/symfony/routing/Tests/Fixtures/list_in_list_defaults.xml b/lib/silex/vendor/symfony/routing/Tests/Fixtures/list_in_list_defaults.xml new file mode 100644 index 000000000..987086dbd --- /dev/null +++ b/lib/silex/vendor/symfony/routing/Tests/Fixtures/list_in_list_defaults.xml @@ -0,0 +1,22 @@ + + + + + + AcmeBlogBundle:Blog:index + + + + + true + 1 + 3.5 + foo + + + + + diff --git a/lib/silex/vendor/symfony/routing/Tests/Fixtures/list_in_map_defaults.xml b/lib/silex/vendor/symfony/routing/Tests/Fixtures/list_in_map_defaults.xml new file mode 100644 index 000000000..32d393c56 --- /dev/null +++ b/lib/silex/vendor/symfony/routing/Tests/Fixtures/list_in_map_defaults.xml @@ -0,0 +1,22 @@ + + + + + + AcmeBlogBundle:Blog:index + + + + + true + 1 + 3.5 + foo + + + + + diff --git a/lib/silex/vendor/symfony/routing/Tests/Fixtures/list_null_values.xml b/lib/silex/vendor/symfony/routing/Tests/Fixtures/list_null_values.xml new file mode 100644 index 000000000..c70e03ccc --- /dev/null +++ b/lib/silex/vendor/symfony/routing/Tests/Fixtures/list_null_values.xml @@ -0,0 +1,22 @@ + + + + + + AcmeBlogBundle:Blog:index + + + + + + + + + + + + + diff --git a/lib/silex/vendor/symfony/routing/Tests/Fixtures/map_defaults.xml b/lib/silex/vendor/symfony/routing/Tests/Fixtures/map_defaults.xml new file mode 100644 index 000000000..47feb29b9 --- /dev/null +++ b/lib/silex/vendor/symfony/routing/Tests/Fixtures/map_defaults.xml @@ -0,0 +1,20 @@ + + + + + + AcmeBlogBundle:Blog:index + + + + true + 1 + 3.5 + foo + + + + diff --git a/lib/silex/vendor/symfony/routing/Tests/Fixtures/map_in_list_defaults.xml b/lib/silex/vendor/symfony/routing/Tests/Fixtures/map_in_list_defaults.xml new file mode 100644 index 000000000..6d770653b --- /dev/null +++ b/lib/silex/vendor/symfony/routing/Tests/Fixtures/map_in_list_defaults.xml @@ -0,0 +1,22 @@ + + + + + + AcmeBlogBundle:Blog:index + + + + + true + 1 + 3.5 + foo + + + + + diff --git a/lib/silex/vendor/symfony/routing/Tests/Fixtures/map_in_map_defaults.xml b/lib/silex/vendor/symfony/routing/Tests/Fixtures/map_in_map_defaults.xml new file mode 100644 index 000000000..2beee6143 --- /dev/null +++ b/lib/silex/vendor/symfony/routing/Tests/Fixtures/map_in_map_defaults.xml @@ -0,0 +1,22 @@ + + + + + + AcmeBlogBundle:Blog:index + + + + + true + 1 + 3.5 + foo + + + + + diff --git a/lib/silex/vendor/symfony/routing/Tests/Fixtures/map_null_values.xml b/lib/silex/vendor/symfony/routing/Tests/Fixtures/map_null_values.xml new file mode 100644 index 000000000..8fd8954e0 --- /dev/null +++ b/lib/silex/vendor/symfony/routing/Tests/Fixtures/map_null_values.xml @@ -0,0 +1,22 @@ + + + + + + AcmeBlogBundle:Blog:index + + + + + + + + + + + + + diff --git a/lib/silex/vendor/symfony/routing/Tests/Fixtures/missing_id.xml b/lib/silex/vendor/symfony/routing/Tests/Fixtures/missing_id.xml new file mode 100644 index 000000000..4ea4115f2 --- /dev/null +++ b/lib/silex/vendor/symfony/routing/Tests/Fixtures/missing_id.xml @@ -0,0 +1,8 @@ + + + + + + diff --git a/lib/silex/vendor/symfony/routing/Tests/Fixtures/missing_path.xml b/lib/silex/vendor/symfony/routing/Tests/Fixtures/missing_path.xml new file mode 100644 index 000000000..ef5bc088d --- /dev/null +++ b/lib/silex/vendor/symfony/routing/Tests/Fixtures/missing_path.xml @@ -0,0 +1,8 @@ + + + + + + diff --git a/lib/silex/vendor/symfony/routing/Tests/Fixtures/namespaceprefix.xml b/lib/silex/vendor/symfony/routing/Tests/Fixtures/namespaceprefix.xml new file mode 100644 index 000000000..e33955ae4 --- /dev/null +++ b/lib/silex/vendor/symfony/routing/Tests/Fixtures/namespaceprefix.xml @@ -0,0 +1,16 @@ + + + + + + MyBundle:Blog:show + \w+ + en|fr|de + RouteCompiler + + 1 + + + diff --git a/lib/silex/vendor/symfony/routing/Tests/Fixtures/nonesense_resource_plus_path.yml b/lib/silex/vendor/symfony/routing/Tests/Fixtures/nonesense_resource_plus_path.yml new file mode 100644 index 000000000..a3e947372 --- /dev/null +++ b/lib/silex/vendor/symfony/routing/Tests/Fixtures/nonesense_resource_plus_path.yml @@ -0,0 +1,3 @@ +blog_show: + resource: validpattern.yml + path: /test diff --git a/lib/silex/vendor/symfony/routing/Tests/Fixtures/nonesense_type_without_resource.yml b/lib/silex/vendor/symfony/routing/Tests/Fixtures/nonesense_type_without_resource.yml new file mode 100644 index 000000000..547cda3b6 --- /dev/null +++ b/lib/silex/vendor/symfony/routing/Tests/Fixtures/nonesense_type_without_resource.yml @@ -0,0 +1,3 @@ +blog_show: + path: /blog/{slug} + type: custom diff --git a/lib/silex/vendor/symfony/routing/Tests/Fixtures/nonvalid.xml b/lib/silex/vendor/symfony/routing/Tests/Fixtures/nonvalid.xml new file mode 100644 index 000000000..dc147d2e6 --- /dev/null +++ b/lib/silex/vendor/symfony/routing/Tests/Fixtures/nonvalid.xml @@ -0,0 +1,10 @@ + + + + + + MyBundle:Blog:show + + diff --git a/lib/silex/vendor/symfony/routing/Tests/Fixtures/nonvalid.yml b/lib/silex/vendor/symfony/routing/Tests/Fixtures/nonvalid.yml new file mode 100644 index 000000000..257cc5642 --- /dev/null +++ b/lib/silex/vendor/symfony/routing/Tests/Fixtures/nonvalid.yml @@ -0,0 +1 @@ +foo diff --git a/lib/silex/vendor/symfony/routing/Tests/Fixtures/nonvalid2.yml b/lib/silex/vendor/symfony/routing/Tests/Fixtures/nonvalid2.yml new file mode 100644 index 000000000..cfa9992bb --- /dev/null +++ b/lib/silex/vendor/symfony/routing/Tests/Fixtures/nonvalid2.yml @@ -0,0 +1 @@ +route: string diff --git a/lib/silex/vendor/symfony/routing/Tests/Fixtures/nonvalidkeys.yml b/lib/silex/vendor/symfony/routing/Tests/Fixtures/nonvalidkeys.yml new file mode 100644 index 000000000..015e270fb --- /dev/null +++ b/lib/silex/vendor/symfony/routing/Tests/Fixtures/nonvalidkeys.yml @@ -0,0 +1,3 @@ +someroute: + resource: path/to/some.yml + name_prefix: test_ diff --git a/lib/silex/vendor/symfony/routing/Tests/Fixtures/nonvalidnode.xml b/lib/silex/vendor/symfony/routing/Tests/Fixtures/nonvalidnode.xml new file mode 100644 index 000000000..863ef03b9 --- /dev/null +++ b/lib/silex/vendor/symfony/routing/Tests/Fixtures/nonvalidnode.xml @@ -0,0 +1,8 @@ + + + + + bar + diff --git a/lib/silex/vendor/symfony/routing/Tests/Fixtures/nonvalidroute.xml b/lib/silex/vendor/symfony/routing/Tests/Fixtures/nonvalidroute.xml new file mode 100644 index 000000000..908958c03 --- /dev/null +++ b/lib/silex/vendor/symfony/routing/Tests/Fixtures/nonvalidroute.xml @@ -0,0 +1,12 @@ + + + + + + MyBundle:Blog:show + + baz + + diff --git a/lib/silex/vendor/symfony/routing/Tests/Fixtures/null_values.xml b/lib/silex/vendor/symfony/routing/Tests/Fixtures/null_values.xml new file mode 100644 index 000000000..f9e2aa24d --- /dev/null +++ b/lib/silex/vendor/symfony/routing/Tests/Fixtures/null_values.xml @@ -0,0 +1,12 @@ + + + + + + + foo + bar + + diff --git a/lib/silex/vendor/symfony/routing/Tests/Fixtures/php_dsl.php b/lib/silex/vendor/symfony/routing/Tests/Fixtures/php_dsl.php new file mode 100644 index 000000000..04f6d7ed6 --- /dev/null +++ b/lib/silex/vendor/symfony/routing/Tests/Fixtures/php_dsl.php @@ -0,0 +1,21 @@ +add('foo', '/foo') + ->condition('abc') + ->options(array('utf8' => true)) + ->add('buz', 'zub') + ->controller('foo:act'); + + $routes->import('php_dsl_sub.php') + ->prefix('/sub') + ->requirements(array('id' => '\d+')); + + $routes->add('ouf', '/ouf') + ->schemes(array('https')) + ->methods(array('GET')) + ->defaults(array('id' => 0)); +}; diff --git a/lib/silex/vendor/symfony/routing/Tests/Fixtures/php_dsl_sub.php b/lib/silex/vendor/symfony/routing/Tests/Fixtures/php_dsl_sub.php new file mode 100644 index 000000000..9eb444ded --- /dev/null +++ b/lib/silex/vendor/symfony/routing/Tests/Fixtures/php_dsl_sub.php @@ -0,0 +1,14 @@ +collection('c_') + ->prefix('pub'); + + $add('bar', '/bar'); + + $add->collection('pub_') + ->host('host') + ->add('buz', 'buz'); +}; diff --git a/lib/silex/vendor/symfony/routing/Tests/Fixtures/scalar_defaults.xml b/lib/silex/vendor/symfony/routing/Tests/Fixtures/scalar_defaults.xml new file mode 100644 index 000000000..ecfde2801 --- /dev/null +++ b/lib/silex/vendor/symfony/routing/Tests/Fixtures/scalar_defaults.xml @@ -0,0 +1,33 @@ + + + + + + AcmeBlogBundle:Blog:index + + + + true + + + 1 + + + 3.5 + + + false + + + 1 + + + 0 + + + + + diff --git a/lib/silex/vendor/symfony/routing/Tests/Fixtures/special_route_name.yml b/lib/silex/vendor/symfony/routing/Tests/Fixtures/special_route_name.yml new file mode 100644 index 000000000..78be239aa --- /dev/null +++ b/lib/silex/vendor/symfony/routing/Tests/Fixtures/special_route_name.yml @@ -0,0 +1,2 @@ +"#$péß^a|": + path: "true" diff --git a/lib/silex/vendor/symfony/routing/Tests/Fixtures/validpattern.php b/lib/silex/vendor/symfony/routing/Tests/Fixtures/validpattern.php new file mode 100644 index 000000000..edc16d8c2 --- /dev/null +++ b/lib/silex/vendor/symfony/routing/Tests/Fixtures/validpattern.php @@ -0,0 +1,18 @@ +add('blog_show', new Route( + '/blog/{slug}', + array('_controller' => 'MyBlogBundle:Blog:show'), + array('locale' => '\w+'), + array('compiler_class' => 'RouteCompiler'), + '{locale}.example.com', + array('https'), + array('GET', 'POST', 'put', 'OpTiOnS'), + 'context.getMethod() == "GET"' +)); + +return $collection; diff --git a/lib/silex/vendor/symfony/routing/Tests/Fixtures/validpattern.xml b/lib/silex/vendor/symfony/routing/Tests/Fixtures/validpattern.xml new file mode 100644 index 000000000..dbc72e46d --- /dev/null +++ b/lib/silex/vendor/symfony/routing/Tests/Fixtures/validpattern.xml @@ -0,0 +1,15 @@ + + + + + + MyBundle:Blog:show + \w+ + + context.getMethod() == "GET" + + + + diff --git a/lib/silex/vendor/symfony/routing/Tests/Fixtures/validpattern.yml b/lib/silex/vendor/symfony/routing/Tests/Fixtures/validpattern.yml new file mode 100644 index 000000000..565abaaa2 --- /dev/null +++ b/lib/silex/vendor/symfony/routing/Tests/Fixtures/validpattern.yml @@ -0,0 +1,13 @@ +blog_show: + path: /blog/{slug} + defaults: { _controller: "MyBundle:Blog:show" } + host: "{locale}.example.com" + requirements: { 'locale': '\w+' } + methods: ['GET','POST','put','OpTiOnS'] + schemes: ['https'] + condition: 'context.getMethod() == "GET"' + options: + compiler_class: RouteCompiler + +blog_show_inherited: + path: /blog/{slug} diff --git a/lib/silex/vendor/symfony/routing/Tests/Fixtures/validresource.php b/lib/silex/vendor/symfony/routing/Tests/Fixtures/validresource.php new file mode 100644 index 000000000..482c80b29 --- /dev/null +++ b/lib/silex/vendor/symfony/routing/Tests/Fixtures/validresource.php @@ -0,0 +1,18 @@ +import('validpattern.php'); +$collection->addDefaults(array( + 'foo' => 123, +)); +$collection->addRequirements(array( + 'foo' => '\d+', +)); +$collection->addOptions(array( + 'foo' => 'bar', +)); +$collection->setCondition('context.getMethod() == "POST"'); +$collection->addPrefix('/prefix'); + +return $collection; diff --git a/lib/silex/vendor/symfony/routing/Tests/Fixtures/validresource.xml b/lib/silex/vendor/symfony/routing/Tests/Fixtures/validresource.xml new file mode 100644 index 000000000..b7a15ddc7 --- /dev/null +++ b/lib/silex/vendor/symfony/routing/Tests/Fixtures/validresource.xml @@ -0,0 +1,13 @@ + + + + + + 123 + \d+ + + context.getMethod() == "POST" + + diff --git a/lib/silex/vendor/symfony/routing/Tests/Fixtures/validresource.yml b/lib/silex/vendor/symfony/routing/Tests/Fixtures/validresource.yml new file mode 100644 index 000000000..faf2263ae --- /dev/null +++ b/lib/silex/vendor/symfony/routing/Tests/Fixtures/validresource.yml @@ -0,0 +1,8 @@ +_blog: + resource: validpattern.yml + prefix: /{foo} + defaults: { 'foo': '123' } + requirements: { 'foo': '\d+' } + options: { 'foo': 'bar' } + host: "" + condition: 'context.getMethod() == "POST"' diff --git a/lib/silex/vendor/symfony/routing/Tests/Fixtures/with_define_path_variable.php b/lib/silex/vendor/symfony/routing/Tests/Fixtures/with_define_path_variable.php new file mode 100644 index 000000000..5871420b1 --- /dev/null +++ b/lib/silex/vendor/symfony/routing/Tests/Fixtures/with_define_path_variable.php @@ -0,0 +1,5 @@ + + + diff --git a/lib/silex/vendor/symfony/routing/Tests/Generator/Dumper/PhpGeneratorDumperTest.php b/lib/silex/vendor/symfony/routing/Tests/Generator/Dumper/PhpGeneratorDumperTest.php new file mode 100644 index 000000000..f84802b35 --- /dev/null +++ b/lib/silex/vendor/symfony/routing/Tests/Generator/Dumper/PhpGeneratorDumperTest.php @@ -0,0 +1,181 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Routing\Tests\Generator\Dumper; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Routing\Generator\UrlGeneratorInterface; +use Symfony\Component\Routing\RouteCollection; +use Symfony\Component\Routing\Route; +use Symfony\Component\Routing\Generator\Dumper\PhpGeneratorDumper; +use Symfony\Component\Routing\RequestContext; + +class PhpGeneratorDumperTest extends TestCase +{ + /** + * @var RouteCollection + */ + private $routeCollection; + + /** + * @var PhpGeneratorDumper + */ + private $generatorDumper; + + /** + * @var string + */ + private $testTmpFilepath; + + /** + * @var string + */ + private $largeTestTmpFilepath; + + protected function setUp() + { + parent::setUp(); + + $this->routeCollection = new RouteCollection(); + $this->generatorDumper = new PhpGeneratorDumper($this->routeCollection); + $this->testTmpFilepath = sys_get_temp_dir().DIRECTORY_SEPARATOR.'php_generator.'.$this->getName().'.php'; + $this->largeTestTmpFilepath = sys_get_temp_dir().DIRECTORY_SEPARATOR.'php_generator.'.$this->getName().'.large.php'; + @unlink($this->testTmpFilepath); + @unlink($this->largeTestTmpFilepath); + } + + protected function tearDown() + { + parent::tearDown(); + + @unlink($this->testTmpFilepath); + + $this->routeCollection = null; + $this->generatorDumper = null; + $this->testTmpFilepath = null; + } + + public function testDumpWithRoutes() + { + $this->routeCollection->add('Test', new Route('/testing/{foo}')); + $this->routeCollection->add('Test2', new Route('/testing2')); + + file_put_contents($this->testTmpFilepath, $this->generatorDumper->dump()); + include $this->testTmpFilepath; + + $projectUrlGenerator = new \ProjectUrlGenerator(new RequestContext('/app.php')); + + $absoluteUrlWithParameter = $projectUrlGenerator->generate('Test', array('foo' => 'bar'), UrlGeneratorInterface::ABSOLUTE_URL); + $absoluteUrlWithoutParameter = $projectUrlGenerator->generate('Test2', array(), UrlGeneratorInterface::ABSOLUTE_URL); + $relativeUrlWithParameter = $projectUrlGenerator->generate('Test', array('foo' => 'bar'), UrlGeneratorInterface::ABSOLUTE_PATH); + $relativeUrlWithoutParameter = $projectUrlGenerator->generate('Test2', array(), UrlGeneratorInterface::ABSOLUTE_PATH); + + $this->assertEquals('http://localhost/app.php/testing/bar', $absoluteUrlWithParameter); + $this->assertEquals('http://localhost/app.php/testing2', $absoluteUrlWithoutParameter); + $this->assertEquals('/app.php/testing/bar', $relativeUrlWithParameter); + $this->assertEquals('/app.php/testing2', $relativeUrlWithoutParameter); + } + + public function testDumpWithTooManyRoutes() + { + if (defined('HHVM_VERSION_ID')) { + $this->markTestSkipped('HHVM consumes too much memory on this test.'); + } + + $this->routeCollection->add('Test', new Route('/testing/{foo}')); + for ($i = 0; $i < 32769; ++$i) { + $this->routeCollection->add('route_'.$i, new Route('/route_'.$i)); + } + $this->routeCollection->add('Test2', new Route('/testing2')); + + file_put_contents($this->largeTestTmpFilepath, $this->generatorDumper->dump(array( + 'class' => 'ProjectLargeUrlGenerator', + ))); + $this->routeCollection = $this->generatorDumper = null; + include $this->largeTestTmpFilepath; + + $projectUrlGenerator = new \ProjectLargeUrlGenerator(new RequestContext('/app.php')); + + $absoluteUrlWithParameter = $projectUrlGenerator->generate('Test', array('foo' => 'bar'), UrlGeneratorInterface::ABSOLUTE_URL); + $absoluteUrlWithoutParameter = $projectUrlGenerator->generate('Test2', array(), UrlGeneratorInterface::ABSOLUTE_URL); + $relativeUrlWithParameter = $projectUrlGenerator->generate('Test', array('foo' => 'bar'), UrlGeneratorInterface::ABSOLUTE_PATH); + $relativeUrlWithoutParameter = $projectUrlGenerator->generate('Test2', array(), UrlGeneratorInterface::ABSOLUTE_PATH); + + $this->assertEquals('http://localhost/app.php/testing/bar', $absoluteUrlWithParameter); + $this->assertEquals('http://localhost/app.php/testing2', $absoluteUrlWithoutParameter); + $this->assertEquals('/app.php/testing/bar', $relativeUrlWithParameter); + $this->assertEquals('/app.php/testing2', $relativeUrlWithoutParameter); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testDumpWithoutRoutes() + { + file_put_contents($this->testTmpFilepath, $this->generatorDumper->dump(array('class' => 'WithoutRoutesUrlGenerator'))); + include $this->testTmpFilepath; + + $projectUrlGenerator = new \WithoutRoutesUrlGenerator(new RequestContext('/app.php')); + + $projectUrlGenerator->generate('Test', array()); + } + + /** + * @expectedException \Symfony\Component\Routing\Exception\RouteNotFoundException + */ + public function testGenerateNonExistingRoute() + { + $this->routeCollection->add('Test', new Route('/test')); + + file_put_contents($this->testTmpFilepath, $this->generatorDumper->dump(array('class' => 'NonExistingRoutesUrlGenerator'))); + include $this->testTmpFilepath; + + $projectUrlGenerator = new \NonExistingRoutesUrlGenerator(new RequestContext()); + $url = $projectUrlGenerator->generate('NonExisting', array()); + } + + public function testDumpForRouteWithDefaults() + { + $this->routeCollection->add('Test', new Route('/testing/{foo}', array('foo' => 'bar'))); + + file_put_contents($this->testTmpFilepath, $this->generatorDumper->dump(array('class' => 'DefaultRoutesUrlGenerator'))); + include $this->testTmpFilepath; + + $projectUrlGenerator = new \DefaultRoutesUrlGenerator(new RequestContext()); + $url = $projectUrlGenerator->generate('Test', array()); + + $this->assertEquals('/testing', $url); + } + + public function testDumpWithSchemeRequirement() + { + $this->routeCollection->add('Test1', new Route('/testing', array(), array(), array(), '', array('ftp', 'https'))); + + file_put_contents($this->testTmpFilepath, $this->generatorDumper->dump(array('class' => 'SchemeUrlGenerator'))); + include $this->testTmpFilepath; + + $projectUrlGenerator = new \SchemeUrlGenerator(new RequestContext('/app.php')); + + $absoluteUrl = $projectUrlGenerator->generate('Test1', array(), UrlGeneratorInterface::ABSOLUTE_URL); + $relativeUrl = $projectUrlGenerator->generate('Test1', array(), UrlGeneratorInterface::ABSOLUTE_PATH); + + $this->assertEquals('ftp://localhost/app.php/testing', $absoluteUrl); + $this->assertEquals('ftp://localhost/app.php/testing', $relativeUrl); + + $projectUrlGenerator = new \SchemeUrlGenerator(new RequestContext('/app.php', 'GET', 'localhost', 'https')); + + $absoluteUrl = $projectUrlGenerator->generate('Test1', array(), UrlGeneratorInterface::ABSOLUTE_URL); + $relativeUrl = $projectUrlGenerator->generate('Test1', array(), UrlGeneratorInterface::ABSOLUTE_PATH); + + $this->assertEquals('https://localhost/app.php/testing', $absoluteUrl); + $this->assertEquals('/app.php/testing', $relativeUrl); + } +} diff --git a/lib/silex/vendor/symfony/routing/Tests/Generator/UrlGeneratorTest.php b/lib/silex/vendor/symfony/routing/Tests/Generator/UrlGeneratorTest.php new file mode 100644 index 000000000..68add771b --- /dev/null +++ b/lib/silex/vendor/symfony/routing/Tests/Generator/UrlGeneratorTest.php @@ -0,0 +1,724 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Routing\Tests\Generator; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Routing\RouteCollection; +use Symfony\Component\Routing\Route; +use Symfony\Component\Routing\Generator\UrlGenerator; +use Symfony\Component\Routing\Generator\UrlGeneratorInterface; +use Symfony\Component\Routing\RequestContext; + +class UrlGeneratorTest extends TestCase +{ + public function testAbsoluteUrlWithPort80() + { + $routes = $this->getRoutes('test', new Route('/testing')); + $url = $this->getGenerator($routes)->generate('test', array(), UrlGeneratorInterface::ABSOLUTE_URL); + + $this->assertEquals('http://localhost/app.php/testing', $url); + } + + public function testAbsoluteSecureUrlWithPort443() + { + $routes = $this->getRoutes('test', new Route('/testing')); + $url = $this->getGenerator($routes, array('scheme' => 'https'))->generate('test', array(), UrlGeneratorInterface::ABSOLUTE_URL); + + $this->assertEquals('https://localhost/app.php/testing', $url); + } + + public function testAbsoluteUrlWithNonStandardPort() + { + $routes = $this->getRoutes('test', new Route('/testing')); + $url = $this->getGenerator($routes, array('httpPort' => 8080))->generate('test', array(), UrlGeneratorInterface::ABSOLUTE_URL); + + $this->assertEquals('http://localhost:8080/app.php/testing', $url); + } + + public function testAbsoluteSecureUrlWithNonStandardPort() + { + $routes = $this->getRoutes('test', new Route('/testing')); + $url = $this->getGenerator($routes, array('httpsPort' => 8080, 'scheme' => 'https'))->generate('test', array(), UrlGeneratorInterface::ABSOLUTE_URL); + + $this->assertEquals('https://localhost:8080/app.php/testing', $url); + } + + public function testRelativeUrlWithoutParameters() + { + $routes = $this->getRoutes('test', new Route('/testing')); + $url = $this->getGenerator($routes)->generate('test', array(), UrlGeneratorInterface::ABSOLUTE_PATH); + + $this->assertEquals('/app.php/testing', $url); + } + + public function testRelativeUrlWithParameter() + { + $routes = $this->getRoutes('test', new Route('/testing/{foo}')); + $url = $this->getGenerator($routes)->generate('test', array('foo' => 'bar'), UrlGeneratorInterface::ABSOLUTE_PATH); + + $this->assertEquals('/app.php/testing/bar', $url); + } + + public function testRelativeUrlWithNullParameter() + { + $routes = $this->getRoutes('test', new Route('/testing.{format}', array('format' => null))); + $url = $this->getGenerator($routes)->generate('test', array(), UrlGeneratorInterface::ABSOLUTE_PATH); + + $this->assertEquals('/app.php/testing', $url); + } + + /** + * @expectedException \Symfony\Component\Routing\Exception\InvalidParameterException + */ + public function testRelativeUrlWithNullParameterButNotOptional() + { + $routes = $this->getRoutes('test', new Route('/testing/{foo}/bar', array('foo' => null))); + // This must raise an exception because the default requirement for "foo" is "[^/]+" which is not met with these params. + // Generating path "/testing//bar" would be wrong as matching this route would fail. + $this->getGenerator($routes)->generate('test', array(), UrlGeneratorInterface::ABSOLUTE_PATH); + } + + public function testRelativeUrlWithOptionalZeroParameter() + { + $routes = $this->getRoutes('test', new Route('/testing/{page}')); + $url = $this->getGenerator($routes)->generate('test', array('page' => 0), UrlGeneratorInterface::ABSOLUTE_PATH); + + $this->assertEquals('/app.php/testing/0', $url); + } + + public function testNotPassedOptionalParameterInBetween() + { + $routes = $this->getRoutes('test', new Route('/{slug}/{page}', array('slug' => 'index', 'page' => 0))); + $this->assertSame('/app.php/index/1', $this->getGenerator($routes)->generate('test', array('page' => 1))); + $this->assertSame('/app.php/', $this->getGenerator($routes)->generate('test')); + } + + public function testRelativeUrlWithExtraParameters() + { + $routes = $this->getRoutes('test', new Route('/testing')); + $url = $this->getGenerator($routes)->generate('test', array('foo' => 'bar'), UrlGeneratorInterface::ABSOLUTE_PATH); + + $this->assertEquals('/app.php/testing?foo=bar', $url); + } + + public function testAbsoluteUrlWithExtraParameters() + { + $routes = $this->getRoutes('test', new Route('/testing')); + $url = $this->getGenerator($routes)->generate('test', array('foo' => 'bar'), UrlGeneratorInterface::ABSOLUTE_URL); + + $this->assertEquals('http://localhost/app.php/testing?foo=bar', $url); + } + + public function testUrlWithNullExtraParameters() + { + $routes = $this->getRoutes('test', new Route('/testing')); + $url = $this->getGenerator($routes)->generate('test', array('foo' => null), UrlGeneratorInterface::ABSOLUTE_URL); + + $this->assertEquals('http://localhost/app.php/testing', $url); + } + + public function testUrlWithExtraParametersFromGlobals() + { + $routes = $this->getRoutes('test', new Route('/testing')); + $generator = $this->getGenerator($routes); + $context = new RequestContext('/app.php'); + $context->setParameter('bar', 'bar'); + $generator->setContext($context); + $url = $generator->generate('test', array('foo' => 'bar')); + + $this->assertEquals('/app.php/testing?foo=bar', $url); + } + + public function testUrlWithGlobalParameter() + { + $routes = $this->getRoutes('test', new Route('/testing/{foo}')); + $generator = $this->getGenerator($routes); + $context = new RequestContext('/app.php'); + $context->setParameter('foo', 'bar'); + $generator->setContext($context); + $url = $generator->generate('test', array()); + + $this->assertEquals('/app.php/testing/bar', $url); + } + + public function testGlobalParameterHasHigherPriorityThanDefault() + { + $routes = $this->getRoutes('test', new Route('/{_locale}', array('_locale' => 'en'))); + $generator = $this->getGenerator($routes); + $context = new RequestContext('/app.php'); + $context->setParameter('_locale', 'de'); + $generator->setContext($context); + $url = $generator->generate('test', array()); + + $this->assertSame('/app.php/de', $url); + } + + /** + * @expectedException \Symfony\Component\Routing\Exception\RouteNotFoundException + */ + public function testGenerateWithoutRoutes() + { + $routes = $this->getRoutes('foo', new Route('/testing/{foo}')); + $this->getGenerator($routes)->generate('test', array(), UrlGeneratorInterface::ABSOLUTE_URL); + } + + /** + * @expectedException \Symfony\Component\Routing\Exception\MissingMandatoryParametersException + */ + public function testGenerateForRouteWithoutMandatoryParameter() + { + $routes = $this->getRoutes('test', new Route('/testing/{foo}')); + $this->getGenerator($routes)->generate('test', array(), UrlGeneratorInterface::ABSOLUTE_URL); + } + + /** + * @expectedException \Symfony\Component\Routing\Exception\InvalidParameterException + */ + public function testGenerateForRouteWithInvalidOptionalParameter() + { + $routes = $this->getRoutes('test', new Route('/testing/{foo}', array('foo' => '1'), array('foo' => 'd+'))); + $this->getGenerator($routes)->generate('test', array('foo' => 'bar'), UrlGeneratorInterface::ABSOLUTE_URL); + } + + /** + * @expectedException \Symfony\Component\Routing\Exception\InvalidParameterException + */ + public function testGenerateForRouteWithInvalidParameter() + { + $routes = $this->getRoutes('test', new Route('/testing/{foo}', array(), array('foo' => '1|2'))); + $this->getGenerator($routes)->generate('test', array('foo' => '0'), UrlGeneratorInterface::ABSOLUTE_URL); + } + + public function testGenerateForRouteWithInvalidOptionalParameterNonStrict() + { + $routes = $this->getRoutes('test', new Route('/testing/{foo}', array('foo' => '1'), array('foo' => 'd+'))); + $generator = $this->getGenerator($routes); + $generator->setStrictRequirements(false); + $this->assertNull($generator->generate('test', array('foo' => 'bar'), UrlGeneratorInterface::ABSOLUTE_URL)); + } + + public function testGenerateForRouteWithInvalidOptionalParameterNonStrictWithLogger() + { + $routes = $this->getRoutes('test', new Route('/testing/{foo}', array('foo' => '1'), array('foo' => 'd+'))); + $logger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock(); + $logger->expects($this->once()) + ->method('error'); + $generator = $this->getGenerator($routes, array(), $logger); + $generator->setStrictRequirements(false); + $this->assertNull($generator->generate('test', array('foo' => 'bar'), UrlGeneratorInterface::ABSOLUTE_URL)); + } + + public function testGenerateForRouteWithInvalidParameterButDisabledRequirementsCheck() + { + $routes = $this->getRoutes('test', new Route('/testing/{foo}', array('foo' => '1'), array('foo' => 'd+'))); + $generator = $this->getGenerator($routes); + $generator->setStrictRequirements(null); + $this->assertSame('/app.php/testing/bar', $generator->generate('test', array('foo' => 'bar'))); + } + + /** + * @expectedException \Symfony\Component\Routing\Exception\InvalidParameterException + */ + public function testGenerateForRouteWithInvalidMandatoryParameter() + { + $routes = $this->getRoutes('test', new Route('/testing/{foo}', array(), array('foo' => 'd+'))); + $this->getGenerator($routes)->generate('test', array('foo' => 'bar'), UrlGeneratorInterface::ABSOLUTE_URL); + } + + /** + * @expectedException \Symfony\Component\Routing\Exception\InvalidParameterException + */ + public function testGenerateForRouteWithInvalidUtf8Parameter() + { + $routes = $this->getRoutes('test', new Route('/testing/{foo}', array(), array('foo' => '\pL+'), array('utf8' => true))); + $this->getGenerator($routes)->generate('test', array('foo' => 'abc123'), UrlGeneratorInterface::ABSOLUTE_URL); + } + + /** + * @expectedException \Symfony\Component\Routing\Exception\InvalidParameterException + */ + public function testRequiredParamAndEmptyPassed() + { + $routes = $this->getRoutes('test', new Route('/{slug}', array(), array('slug' => '.+'))); + $this->getGenerator($routes)->generate('test', array('slug' => '')); + } + + public function testSchemeRequirementDoesNothingIfSameCurrentScheme() + { + $routes = $this->getRoutes('test', new Route('/', array(), array(), array(), '', array('http'))); + $this->assertEquals('/app.php/', $this->getGenerator($routes)->generate('test')); + + $routes = $this->getRoutes('test', new Route('/', array(), array(), array(), '', array('https'))); + $this->assertEquals('/app.php/', $this->getGenerator($routes, array('scheme' => 'https'))->generate('test')); + } + + public function testSchemeRequirementForcesAbsoluteUrl() + { + $routes = $this->getRoutes('test', new Route('/', array(), array(), array(), '', array('https'))); + $this->assertEquals('https://localhost/app.php/', $this->getGenerator($routes)->generate('test')); + + $routes = $this->getRoutes('test', new Route('/', array(), array(), array(), '', array('http'))); + $this->assertEquals('http://localhost/app.php/', $this->getGenerator($routes, array('scheme' => 'https'))->generate('test')); + } + + public function testSchemeRequirementCreatesUrlForFirstRequiredScheme() + { + $routes = $this->getRoutes('test', new Route('/', array(), array(), array(), '', array('Ftp', 'https'))); + $this->assertEquals('ftp://localhost/app.php/', $this->getGenerator($routes)->generate('test')); + } + + public function testPathWithTwoStartingSlashes() + { + $routes = $this->getRoutes('test', new Route('//path-and-not-domain')); + + // this must not generate '//path-and-not-domain' because that would be a network path + $this->assertSame('/path-and-not-domain', $this->getGenerator($routes, array('BaseUrl' => ''))->generate('test')); + } + + public function testNoTrailingSlashForMultipleOptionalParameters() + { + $routes = $this->getRoutes('test', new Route('/category/{slug1}/{slug2}/{slug3}', array('slug2' => null, 'slug3' => null))); + + $this->assertEquals('/app.php/category/foo', $this->getGenerator($routes)->generate('test', array('slug1' => 'foo'))); + } + + public function testWithAnIntegerAsADefaultValue() + { + $routes = $this->getRoutes('test', new Route('/{default}', array('default' => 0))); + + $this->assertEquals('/app.php/foo', $this->getGenerator($routes)->generate('test', array('default' => 'foo'))); + } + + public function testNullForOptionalParameterIsIgnored() + { + $routes = $this->getRoutes('test', new Route('/test/{default}', array('default' => 0))); + + $this->assertEquals('/app.php/test', $this->getGenerator($routes)->generate('test', array('default' => null))); + } + + public function testQueryParamSameAsDefault() + { + $routes = $this->getRoutes('test', new Route('/test', array('page' => 1))); + + $this->assertSame('/app.php/test?page=2', $this->getGenerator($routes)->generate('test', array('page' => 2))); + $this->assertSame('/app.php/test', $this->getGenerator($routes)->generate('test', array('page' => 1))); + $this->assertSame('/app.php/test', $this->getGenerator($routes)->generate('test', array('page' => '1'))); + $this->assertSame('/app.php/test', $this->getGenerator($routes)->generate('test')); + } + + public function testArrayQueryParamSameAsDefault() + { + $routes = $this->getRoutes('test', new Route('/test', array('array' => array('foo', 'bar')))); + + $this->assertSame('/app.php/test?array%5B0%5D=bar&array%5B1%5D=foo', $this->getGenerator($routes)->generate('test', array('array' => array('bar', 'foo')))); + $this->assertSame('/app.php/test?array%5Ba%5D=foo&array%5Bb%5D=bar', $this->getGenerator($routes)->generate('test', array('array' => array('a' => 'foo', 'b' => 'bar')))); + $this->assertSame('/app.php/test', $this->getGenerator($routes)->generate('test', array('array' => array('foo', 'bar')))); + $this->assertSame('/app.php/test', $this->getGenerator($routes)->generate('test', array('array' => array(1 => 'bar', 0 => 'foo')))); + $this->assertSame('/app.php/test', $this->getGenerator($routes)->generate('test')); + } + + public function testGenerateWithSpecialRouteName() + { + $routes = $this->getRoutes('$péß^a|', new Route('/bar')); + + $this->assertSame('/app.php/bar', $this->getGenerator($routes)->generate('$péß^a|')); + } + + public function testUrlEncoding() + { + $expectedPath = '/app.php/@:%5B%5D/%28%29*%27%22%20+,;-._~%26%24%3C%3E|%7B%7D%25%5C%5E%60!%3Ffoo=bar%23id' + .'/@:%5B%5D/%28%29*%27%22%20+,;-._~%26%24%3C%3E|%7B%7D%25%5C%5E%60!%3Ffoo=bar%23id' + .'?query=%40%3A%5B%5D/%28%29%2A%27%22%20%2B%2C%3B-._~%26%24%3C%3E%7C%7B%7D%25%5C%5E%60%21%3Ffoo%3Dbar%23id'; + + // This tests the encoding of reserved characters that are used for delimiting of URI components (defined in RFC 3986) + // and other special ASCII chars. These chars are tested as static text path, variable path and query param. + $chars = '@:[]/()*\'" +,;-._~&$<>|{}%\\^`!?foo=bar#id'; + $routes = $this->getRoutes('test', new Route("/$chars/{varpath}", array(), array('varpath' => '.+'))); + $this->assertSame($expectedPath, $this->getGenerator($routes)->generate('test', array( + 'varpath' => $chars, + 'query' => $chars, + ))); + } + + public function testEncodingOfRelativePathSegments() + { + $routes = $this->getRoutes('test', new Route('/dir/../dir/..')); + $this->assertSame('/app.php/dir/%2E%2E/dir/%2E%2E', $this->getGenerator($routes)->generate('test')); + $routes = $this->getRoutes('test', new Route('/dir/./dir/.')); + $this->assertSame('/app.php/dir/%2E/dir/%2E', $this->getGenerator($routes)->generate('test')); + $routes = $this->getRoutes('test', new Route('/a./.a/a../..a/...')); + $this->assertSame('/app.php/a./.a/a../..a/...', $this->getGenerator($routes)->generate('test')); + } + + public function testAdjacentVariables() + { + $routes = $this->getRoutes('test', new Route('/{x}{y}{z}.{_format}', array('z' => 'default-z', '_format' => 'html'), array('y' => '\d+'))); + $generator = $this->getGenerator($routes); + $this->assertSame('/app.php/foo123', $generator->generate('test', array('x' => 'foo', 'y' => '123'))); + $this->assertSame('/app.php/foo123bar.xml', $generator->generate('test', array('x' => 'foo', 'y' => '123', 'z' => 'bar', '_format' => 'xml'))); + + // The default requirement for 'x' should not allow the separator '.' in this case because it would otherwise match everything + // and following optional variables like _format could never match. + $this->{method_exists($this, $_ = 'expectException') ? $_ : 'setExpectedException'}('Symfony\Component\Routing\Exception\InvalidParameterException'); + $generator->generate('test', array('x' => 'do.t', 'y' => '123', 'z' => 'bar', '_format' => 'xml')); + } + + public function testOptionalVariableWithNoRealSeparator() + { + $routes = $this->getRoutes('test', new Route('/get{what}', array('what' => 'All'))); + $generator = $this->getGenerator($routes); + + $this->assertSame('/app.php/get', $generator->generate('test')); + $this->assertSame('/app.php/getSites', $generator->generate('test', array('what' => 'Sites'))); + } + + public function testRequiredVariableWithNoRealSeparator() + { + $routes = $this->getRoutes('test', new Route('/get{what}Suffix')); + $generator = $this->getGenerator($routes); + + $this->assertSame('/app.php/getSitesSuffix', $generator->generate('test', array('what' => 'Sites'))); + } + + public function testDefaultRequirementOfVariable() + { + $routes = $this->getRoutes('test', new Route('/{page}.{_format}')); + $generator = $this->getGenerator($routes); + + $this->assertSame('/app.php/index.mobile.html', $generator->generate('test', array('page' => 'index', '_format' => 'mobile.html'))); + } + + /** + * @expectedException \Symfony\Component\Routing\Exception\InvalidParameterException + */ + public function testDefaultRequirementOfVariableDisallowsSlash() + { + $routes = $this->getRoutes('test', new Route('/{page}.{_format}')); + $this->getGenerator($routes)->generate('test', array('page' => 'index', '_format' => 'sl/ash')); + } + + /** + * @expectedException \Symfony\Component\Routing\Exception\InvalidParameterException + */ + public function testDefaultRequirementOfVariableDisallowsNextSeparator() + { + $routes = $this->getRoutes('test', new Route('/{page}.{_format}')); + $this->getGenerator($routes)->generate('test', array('page' => 'do.t', '_format' => 'html')); + } + + public function testWithHostDifferentFromContext() + { + $routes = $this->getRoutes('test', new Route('/{name}', array(), array(), array(), '{locale}.example.com')); + + $this->assertEquals('//fr.example.com/app.php/Fabien', $this->getGenerator($routes)->generate('test', array('name' => 'Fabien', 'locale' => 'fr'))); + } + + public function testWithHostSameAsContext() + { + $routes = $this->getRoutes('test', new Route('/{name}', array(), array(), array(), '{locale}.example.com')); + + $this->assertEquals('/app.php/Fabien', $this->getGenerator($routes, array('host' => 'fr.example.com'))->generate('test', array('name' => 'Fabien', 'locale' => 'fr'))); + } + + public function testWithHostSameAsContextAndAbsolute() + { + $routes = $this->getRoutes('test', new Route('/{name}', array(), array(), array(), '{locale}.example.com')); + + $this->assertEquals('http://fr.example.com/app.php/Fabien', $this->getGenerator($routes, array('host' => 'fr.example.com'))->generate('test', array('name' => 'Fabien', 'locale' => 'fr'), UrlGeneratorInterface::ABSOLUTE_URL)); + } + + /** + * @expectedException \Symfony\Component\Routing\Exception\InvalidParameterException + */ + public function testUrlWithInvalidParameterInHost() + { + $routes = $this->getRoutes('test', new Route('/', array(), array('foo' => 'bar'), array(), '{foo}.example.com')); + $this->getGenerator($routes)->generate('test', array('foo' => 'baz'), UrlGeneratorInterface::ABSOLUTE_PATH); + } + + /** + * @expectedException \Symfony\Component\Routing\Exception\InvalidParameterException + */ + public function testUrlWithInvalidParameterInHostWhenParamHasADefaultValue() + { + $routes = $this->getRoutes('test', new Route('/', array('foo' => 'bar'), array('foo' => 'bar'), array(), '{foo}.example.com')); + $this->getGenerator($routes)->generate('test', array('foo' => 'baz'), UrlGeneratorInterface::ABSOLUTE_PATH); + } + + /** + * @expectedException \Symfony\Component\Routing\Exception\InvalidParameterException + */ + public function testUrlWithInvalidParameterEqualsDefaultValueInHost() + { + $routes = $this->getRoutes('test', new Route('/', array('foo' => 'baz'), array('foo' => 'bar'), array(), '{foo}.example.com')); + $this->getGenerator($routes)->generate('test', array('foo' => 'baz'), UrlGeneratorInterface::ABSOLUTE_PATH); + } + + public function testUrlWithInvalidParameterInHostInNonStrictMode() + { + $routes = $this->getRoutes('test', new Route('/', array(), array('foo' => 'bar'), array(), '{foo}.example.com')); + $generator = $this->getGenerator($routes); + $generator->setStrictRequirements(false); + $this->assertNull($generator->generate('test', array('foo' => 'baz'), UrlGeneratorInterface::ABSOLUTE_PATH)); + } + + public function testHostIsCaseInsensitive() + { + $routes = $this->getRoutes('test', new Route('/', array(), array('locale' => 'en|de|fr'), array(), '{locale}.FooBar.com')); + $generator = $this->getGenerator($routes); + $this->assertSame('//EN.FooBar.com/app.php/', $generator->generate('test', array('locale' => 'EN'), UrlGeneratorInterface::NETWORK_PATH)); + } + + public function testDefaultHostIsUsedWhenContextHostIsEmpty() + { + $routes = $this->getRoutes('test', new Route('/route', array('domain' => 'my.fallback.host'), array('domain' => '.+'), array(), '{domain}', array('http'))); + + $generator = $this->getGenerator($routes); + $generator->getContext()->setHost(''); + + $this->assertSame('http://my.fallback.host/app.php/route', $generator->generate('test', array(), UrlGeneratorInterface::ABSOLUTE_URL)); + } + + public function testDefaultHostIsUsedWhenContextHostIsEmptyAndSchemeIsNot() + { + $routes = $this->getRoutes('test', new Route('/route', array('domain' => 'my.fallback.host'), array('domain' => '.+'), array(), '{domain}', array('http', 'https'))); + + $generator = $this->getGenerator($routes); + $generator->getContext()->setHost(''); + $generator->getContext()->setScheme('https'); + + $this->assertSame('https://my.fallback.host/app.php/route', $generator->generate('test', array(), UrlGeneratorInterface::ABSOLUTE_URL)); + } + + public function testAbsoluteUrlFallbackToRelativeIfHostIsEmptyAndSchemeIsNot() + { + $routes = $this->getRoutes('test', new Route('/route', array(), array(), array(), '', array('http', 'https'))); + + $generator = $this->getGenerator($routes); + $generator->getContext()->setHost(''); + $generator->getContext()->setScheme('https'); + + $this->assertSame('/app.php/route', $generator->generate('test', array(), UrlGeneratorInterface::ABSOLUTE_URL)); + } + + public function testGenerateNetworkPath() + { + $routes = $this->getRoutes('test', new Route('/{name}', array(), array(), array(), '{locale}.example.com', array('http'))); + + $this->assertSame('//fr.example.com/app.php/Fabien', $this->getGenerator($routes)->generate('test', + array('name' => 'Fabien', 'locale' => 'fr'), UrlGeneratorInterface::NETWORK_PATH), 'network path with different host' + ); + $this->assertSame('//fr.example.com/app.php/Fabien?query=string', $this->getGenerator($routes, array('host' => 'fr.example.com'))->generate('test', + array('name' => 'Fabien', 'locale' => 'fr', 'query' => 'string'), UrlGeneratorInterface::NETWORK_PATH), 'network path although host same as context' + ); + $this->assertSame('http://fr.example.com/app.php/Fabien', $this->getGenerator($routes, array('scheme' => 'https'))->generate('test', + array('name' => 'Fabien', 'locale' => 'fr'), UrlGeneratorInterface::NETWORK_PATH), 'absolute URL because scheme requirement does not match context' + ); + $this->assertSame('http://fr.example.com/app.php/Fabien', $this->getGenerator($routes)->generate('test', + array('name' => 'Fabien', 'locale' => 'fr'), UrlGeneratorInterface::ABSOLUTE_URL), 'absolute URL with same scheme because it is requested' + ); + } + + public function testGenerateRelativePath() + { + $routes = new RouteCollection(); + $routes->add('article', new Route('/{author}/{article}/')); + $routes->add('comments', new Route('/{author}/{article}/comments')); + $routes->add('host', new Route('/{article}', array(), array(), array(), '{author}.example.com')); + $routes->add('scheme', new Route('/{author}/blog', array(), array(), array(), '', array('https'))); + $routes->add('unrelated', new Route('/about')); + + $generator = $this->getGenerator($routes, array('host' => 'example.com', 'pathInfo' => '/fabien/symfony-is-great/')); + + $this->assertSame('comments', $generator->generate('comments', + array('author' => 'fabien', 'article' => 'symfony-is-great'), UrlGeneratorInterface::RELATIVE_PATH) + ); + $this->assertSame('comments?page=2', $generator->generate('comments', + array('author' => 'fabien', 'article' => 'symfony-is-great', 'page' => 2), UrlGeneratorInterface::RELATIVE_PATH) + ); + $this->assertSame('../twig-is-great/', $generator->generate('article', + array('author' => 'fabien', 'article' => 'twig-is-great'), UrlGeneratorInterface::RELATIVE_PATH) + ); + $this->assertSame('../../bernhard/forms-are-great/', $generator->generate('article', + array('author' => 'bernhard', 'article' => 'forms-are-great'), UrlGeneratorInterface::RELATIVE_PATH) + ); + $this->assertSame('//bernhard.example.com/app.php/forms-are-great', $generator->generate('host', + array('author' => 'bernhard', 'article' => 'forms-are-great'), UrlGeneratorInterface::RELATIVE_PATH) + ); + $this->assertSame('https://example.com/app.php/bernhard/blog', $generator->generate('scheme', + array('author' => 'bernhard'), UrlGeneratorInterface::RELATIVE_PATH) + ); + $this->assertSame('../../about', $generator->generate('unrelated', + array(), UrlGeneratorInterface::RELATIVE_PATH) + ); + } + + /** + * @dataProvider provideRelativePaths + */ + public function testGetRelativePath($sourcePath, $targetPath, $expectedPath) + { + $this->assertSame($expectedPath, UrlGenerator::getRelativePath($sourcePath, $targetPath)); + } + + public function provideRelativePaths() + { + return array( + array( + '/same/dir/', + '/same/dir/', + '', + ), + array( + '/same/file', + '/same/file', + '', + ), + array( + '/', + '/file', + 'file', + ), + array( + '/', + '/dir/file', + 'dir/file', + ), + array( + '/dir/file.html', + '/dir/different-file.html', + 'different-file.html', + ), + array( + '/same/dir/extra-file', + '/same/dir/', + './', + ), + array( + '/parent/dir/', + '/parent/', + '../', + ), + array( + '/parent/dir/extra-file', + '/parent/', + '../', + ), + array( + '/a/b/', + '/x/y/z/', + '../../x/y/z/', + ), + array( + '/a/b/c/d/e', + '/a/c/d', + '../../../c/d', + ), + array( + '/a/b/c//', + '/a/b/c/', + '../', + ), + array( + '/a/b/c/', + '/a/b/c//', + './/', + ), + array( + '/root/a/b/c/', + '/root/x/b/c/', + '../../../x/b/c/', + ), + array( + '/a/b/c/d/', + '/a', + '../../../../a', + ), + array( + '/special-chars/sp%20ce/1€/mäh/e=mc²', + '/special-chars/sp%20ce/1€/<µ>/e=mc²', + '../<µ>/e=mc²', + ), + array( + 'not-rooted', + 'dir/file', + 'dir/file', + ), + array( + '//dir/', + '', + '../../', + ), + array( + '/dir/', + '/dir/file:with-colon', + './file:with-colon', + ), + array( + '/dir/', + '/dir/subdir/file:with-colon', + 'subdir/file:with-colon', + ), + array( + '/dir/', + '/dir/:subdir/', + './:subdir/', + ), + ); + } + + public function testFragmentsCanBeAppendedToUrls() + { + $routes = $this->getRoutes('test', new Route('/testing')); + + $url = $this->getGenerator($routes)->generate('test', array('_fragment' => 'frag ment'), UrlGeneratorInterface::ABSOLUTE_PATH); + $this->assertEquals('/app.php/testing#frag%20ment', $url); + + $url = $this->getGenerator($routes)->generate('test', array('_fragment' => '0'), UrlGeneratorInterface::ABSOLUTE_PATH); + $this->assertEquals('/app.php/testing#0', $url); + } + + public function testFragmentsDoNotEscapeValidCharacters() + { + $routes = $this->getRoutes('test', new Route('/testing')); + $url = $this->getGenerator($routes)->generate('test', array('_fragment' => '?/'), UrlGeneratorInterface::ABSOLUTE_PATH); + + $this->assertEquals('/app.php/testing#?/', $url); + } + + public function testFragmentsCanBeDefinedAsDefaults() + { + $routes = $this->getRoutes('test', new Route('/testing', array('_fragment' => 'fragment'))); + $url = $this->getGenerator($routes)->generate('test', array(), UrlGeneratorInterface::ABSOLUTE_PATH); + + $this->assertEquals('/app.php/testing#fragment', $url); + } + + protected function getGenerator(RouteCollection $routes, array $parameters = array(), $logger = null) + { + $context = new RequestContext('/app.php'); + foreach ($parameters as $key => $value) { + $method = 'set'.$key; + $context->$method($value); + } + + return new UrlGenerator($routes, $context, $logger); + } + + protected function getRoutes($name, Route $route) + { + $routes = new RouteCollection(); + $routes->add($name, $route); + + return $routes; + } +} diff --git a/lib/silex/vendor/symfony/routing/Tests/Loader/AbstractAnnotationLoaderTest.php b/lib/silex/vendor/symfony/routing/Tests/Loader/AbstractAnnotationLoaderTest.php new file mode 100644 index 000000000..e8bbe8fcf --- /dev/null +++ b/lib/silex/vendor/symfony/routing/Tests/Loader/AbstractAnnotationLoaderTest.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\Routing\Tests\Loader; + +use PHPUnit\Framework\TestCase; + +abstract class AbstractAnnotationLoaderTest extends TestCase +{ + public function getReader() + { + return $this->getMockBuilder('Doctrine\Common\Annotations\Reader') + ->disableOriginalConstructor() + ->getMock() + ; + } + + public function getClassLoader($reader) + { + return $this->getMockBuilder('Symfony\Component\Routing\Loader\AnnotationClassLoader') + ->setConstructorArgs(array($reader)) + ->getMockForAbstractClass() + ; + } +} diff --git a/lib/silex/vendor/symfony/routing/Tests/Loader/AnnotationClassLoaderTest.php b/lib/silex/vendor/symfony/routing/Tests/Loader/AnnotationClassLoaderTest.php new file mode 100644 index 000000000..70db1ccd9 --- /dev/null +++ b/lib/silex/vendor/symfony/routing/Tests/Loader/AnnotationClassLoaderTest.php @@ -0,0 +1,255 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Routing\Tests\Loader; + +use Symfony\Component\Routing\Annotation\Route; + +class AnnotationClassLoaderTest extends AbstractAnnotationLoaderTest +{ + protected $loader; + private $reader; + + protected function setUp() + { + parent::setUp(); + + $this->reader = $this->getReader(); + $this->loader = $this->getClassLoader($this->reader); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testLoadMissingClass() + { + $this->loader->load('MissingClass'); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testLoadAbstractClass() + { + $this->loader->load('Symfony\Component\Routing\Tests\Fixtures\AnnotatedClasses\AbstractClass'); + } + + /** + * @dataProvider provideTestSupportsChecksResource + */ + public function testSupportsChecksResource($resource, $expectedSupports) + { + $this->assertSame($expectedSupports, $this->loader->supports($resource), '->supports() returns true if the resource is loadable'); + } + + public function provideTestSupportsChecksResource() + { + return array( + array('class', true), + array('\fully\qualified\class\name', true), + array('namespaced\class\without\leading\slash', true), + array('ÿClassWithLegalSpecialCharacters', true), + array('5', false), + array('foo.foo', false), + array(null, false), + ); + } + + public function testSupportsChecksTypeIfSpecified() + { + $this->assertTrue($this->loader->supports('class', 'annotation'), '->supports() checks the resource type if specified'); + $this->assertFalse($this->loader->supports('class', 'foo'), '->supports() checks the resource type if specified'); + } + + public function getLoadTests() + { + return array( + array( + 'Symfony\Component\Routing\Tests\Fixtures\AnnotatedClasses\BarClass', + array('name' => 'route1', 'path' => '/path'), + array('arg2' => 'defaultValue2', 'arg3' => 'defaultValue3'), + ), + array( + 'Symfony\Component\Routing\Tests\Fixtures\AnnotatedClasses\BarClass', + array('defaults' => array('arg2' => 'foo'), 'requirements' => array('arg3' => '\w+')), + array('arg2' => 'defaultValue2', 'arg3' => 'defaultValue3'), + ), + array( + 'Symfony\Component\Routing\Tests\Fixtures\AnnotatedClasses\BarClass', + array('options' => array('foo' => 'bar')), + array('arg2' => 'defaultValue2', 'arg3' => 'defaultValue3'), + ), + array( + 'Symfony\Component\Routing\Tests\Fixtures\AnnotatedClasses\BarClass', + array('schemes' => array('https'), 'methods' => array('GET')), + array('arg2' => 'defaultValue2', 'arg3' => 'defaultValue3'), + ), + array( + 'Symfony\Component\Routing\Tests\Fixtures\AnnotatedClasses\BarClass', + array('condition' => 'context.getMethod() == "GET"'), + array('arg2' => 'defaultValue2', 'arg3' => 'defaultValue3'), + ), + ); + } + + /** + * @dataProvider getLoadTests + */ + public function testLoad($className, $routeData = array(), $methodArgs = array()) + { + $routeData = array_replace(array( + 'name' => 'route', + 'path' => '/', + 'requirements' => array(), + 'options' => array(), + 'defaults' => array(), + 'schemes' => array(), + 'methods' => array(), + 'condition' => '', + ), $routeData); + + $this->reader + ->expects($this->once()) + ->method('getMethodAnnotations') + ->will($this->returnValue(array($this->getAnnotatedRoute($routeData)))) + ; + + $routeCollection = $this->loader->load($className); + $route = $routeCollection->get($routeData['name']); + + $this->assertSame($routeData['path'], $route->getPath(), '->load preserves path annotation'); + $this->assertCount( + count($routeData['requirements']), + array_intersect_assoc($routeData['requirements'], $route->getRequirements()), + '->load preserves requirements annotation' + ); + $this->assertCount( + count($routeData['options']), + array_intersect_assoc($routeData['options'], $route->getOptions()), + '->load preserves options annotation' + ); + $this->assertCount( + count($routeData['defaults']), + $route->getDefaults(), + '->load preserves defaults annotation' + ); + $this->assertEquals($routeData['schemes'], $route->getSchemes(), '->load preserves schemes annotation'); + $this->assertEquals($routeData['methods'], $route->getMethods(), '->load preserves methods annotation'); + $this->assertSame($routeData['condition'], $route->getCondition(), '->load preserves condition annotation'); + } + + public function testClassRouteLoad() + { + $classRouteData = array( + 'name' => 'prefix_', + 'path' => '/prefix', + 'schemes' => array('https'), + 'methods' => array('GET'), + ); + + $methodRouteData = array( + 'name' => 'route1', + 'path' => '/path', + 'schemes' => array('http'), + 'methods' => array('POST', 'PUT'), + ); + + $this->reader + ->expects($this->once()) + ->method('getClassAnnotation') + ->will($this->returnValue($this->getAnnotatedRoute($classRouteData))) + ; + $this->reader + ->expects($this->once()) + ->method('getMethodAnnotations') + ->will($this->returnValue(array($this->getAnnotatedRoute($methodRouteData)))) + ; + + $routeCollection = $this->loader->load('Symfony\Component\Routing\Tests\Fixtures\AnnotatedClasses\BarClass'); + $route = $routeCollection->get($classRouteData['name'].$methodRouteData['name']); + + $this->assertSame($classRouteData['path'].$methodRouteData['path'], $route->getPath(), '->load concatenates class and method route path'); + $this->assertEquals(array_merge($classRouteData['schemes'], $methodRouteData['schemes']), $route->getSchemes(), '->load merges class and method route schemes'); + $this->assertEquals(array_merge($classRouteData['methods'], $methodRouteData['methods']), $route->getMethods(), '->load merges class and method route methods'); + } + + public function testInvokableClassRouteLoad() + { + $classRouteData = array( + 'name' => 'route1', + 'path' => '/', + 'schemes' => array('https'), + 'methods' => array('GET'), + ); + + $this->reader + ->expects($this->exactly(2)) + ->method('getClassAnnotation') + ->will($this->returnValue($this->getAnnotatedRoute($classRouteData))) + ; + $this->reader + ->expects($this->once()) + ->method('getMethodAnnotations') + ->will($this->returnValue(array())) + ; + + $routeCollection = $this->loader->load('Symfony\Component\Routing\Tests\Fixtures\AnnotatedClasses\BazClass'); + $route = $routeCollection->get($classRouteData['name']); + + $this->assertSame($classRouteData['path'], $route->getPath(), '->load preserves class route path'); + $this->assertEquals(array_merge($classRouteData['schemes'], $classRouteData['schemes']), $route->getSchemes(), '->load preserves class route schemes'); + $this->assertEquals(array_merge($classRouteData['methods'], $classRouteData['methods']), $route->getMethods(), '->load preserves class route methods'); + } + + public function testInvokableClassWithMethodRouteLoad() + { + $classRouteData = array( + 'name' => 'route1', + 'path' => '/prefix', + 'schemes' => array('https'), + 'methods' => array('GET'), + ); + + $methodRouteData = array( + 'name' => 'route2', + 'path' => '/path', + 'schemes' => array('http'), + 'methods' => array('POST', 'PUT'), + ); + + $this->reader + ->expects($this->once()) + ->method('getClassAnnotation') + ->will($this->returnValue($this->getAnnotatedRoute($classRouteData))) + ; + $this->reader + ->expects($this->once()) + ->method('getMethodAnnotations') + ->will($this->returnValue(array($this->getAnnotatedRoute($methodRouteData)))) + ; + + $routeCollection = $this->loader->load('Symfony\Component\Routing\Tests\Fixtures\AnnotatedClasses\BazClass'); + $route = $routeCollection->get($classRouteData['name']); + + $this->assertNull($route, '->load ignores class route'); + + $route = $routeCollection->get($classRouteData['name'].$methodRouteData['name']); + + $this->assertSame($classRouteData['path'].$methodRouteData['path'], $route->getPath(), '->load concatenates class and method route path'); + $this->assertEquals(array_merge($classRouteData['schemes'], $methodRouteData['schemes']), $route->getSchemes(), '->load merges class and method route schemes'); + $this->assertEquals(array_merge($classRouteData['methods'], $methodRouteData['methods']), $route->getMethods(), '->load merges class and method route methods'); + } + + private function getAnnotatedRoute($data) + { + return new Route($data); + } +} diff --git a/lib/silex/vendor/symfony/routing/Tests/Loader/AnnotationDirectoryLoaderTest.php b/lib/silex/vendor/symfony/routing/Tests/Loader/AnnotationDirectoryLoaderTest.php new file mode 100644 index 000000000..1e8ee3940 --- /dev/null +++ b/lib/silex/vendor/symfony/routing/Tests/Loader/AnnotationDirectoryLoaderTest.php @@ -0,0 +1,98 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Routing\Tests\Loader; + +use Symfony\Component\Routing\Loader\AnnotationDirectoryLoader; +use Symfony\Component\Config\FileLocator; + +class AnnotationDirectoryLoaderTest extends AbstractAnnotationLoaderTest +{ + protected $loader; + protected $reader; + + protected function setUp() + { + parent::setUp(); + + $this->reader = $this->getReader(); + $this->loader = new AnnotationDirectoryLoader(new FileLocator(), $this->getClassLoader($this->reader)); + } + + public function testLoad() + { + $this->reader->expects($this->exactly(4))->method('getClassAnnotation'); + + $this->reader + ->expects($this->any()) + ->method('getMethodAnnotations') + ->will($this->returnValue(array())) + ; + + $this->loader->load(__DIR__.'/../Fixtures/AnnotatedClasses'); + } + + public function testLoadIgnoresHiddenDirectories() + { + $this->expectAnnotationsToBeReadFrom(array( + 'Symfony\Component\Routing\Tests\Fixtures\AnnotatedClasses\BarClass', + 'Symfony\Component\Routing\Tests\Fixtures\AnnotatedClasses\BazClass', + 'Symfony\Component\Routing\Tests\Fixtures\AnnotatedClasses\BazClass', + 'Symfony\Component\Routing\Tests\Fixtures\AnnotatedClasses\FooClass', + )); + + $this->reader + ->expects($this->any()) + ->method('getMethodAnnotations') + ->will($this->returnValue(array())) + ; + + $this->loader->load(__DIR__.'/../Fixtures/AnnotatedClasses'); + } + + public function testSupports() + { + $fixturesDir = __DIR__.'/../Fixtures'; + + $this->assertTrue($this->loader->supports($fixturesDir), '->supports() returns true if the resource is loadable'); + $this->assertFalse($this->loader->supports('foo.foo'), '->supports() returns true if the resource is loadable'); + + $this->assertTrue($this->loader->supports($fixturesDir, 'annotation'), '->supports() checks the resource type if specified'); + $this->assertFalse($this->loader->supports($fixturesDir, 'foo'), '->supports() checks the resource type if specified'); + } + + public function testItSupportsAnyAnnotation() + { + $this->assertTrue($this->loader->supports(__DIR__.'/../Fixtures/even-with-not-existing-folder', 'annotation')); + } + + public function testLoadFileIfLocatedResourceIsFile() + { + $this->reader->expects($this->exactly(1))->method('getClassAnnotation'); + + $this->reader + ->expects($this->any()) + ->method('getMethodAnnotations') + ->will($this->returnValue(array())) + ; + + $this->loader->load(__DIR__.'/../Fixtures/AnnotatedClasses/FooClass.php'); + } + + private function expectAnnotationsToBeReadFrom(array $classes) + { + $this->reader->expects($this->exactly(count($classes))) + ->method('getClassAnnotation') + ->with($this->callback(function (\ReflectionClass $class) use ($classes) { + return in_array($class->getName(), $classes); + })); + } +} diff --git a/lib/silex/vendor/symfony/routing/Tests/Loader/AnnotationFileLoaderTest.php b/lib/silex/vendor/symfony/routing/Tests/Loader/AnnotationFileLoaderTest.php new file mode 100644 index 000000000..7f1d57655 --- /dev/null +++ b/lib/silex/vendor/symfony/routing/Tests/Loader/AnnotationFileLoaderTest.php @@ -0,0 +1,91 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Routing\Tests\Loader; + +use Symfony\Component\Routing\Loader\AnnotationFileLoader; +use Symfony\Component\Config\FileLocator; +use Symfony\Component\Routing\Annotation\Route; + +class AnnotationFileLoaderTest extends AbstractAnnotationLoaderTest +{ + protected $loader; + protected $reader; + + protected function setUp() + { + parent::setUp(); + + $this->reader = $this->getReader(); + $this->loader = new AnnotationFileLoader(new FileLocator(), $this->getClassLoader($this->reader)); + } + + public function testLoad() + { + $this->reader->expects($this->once())->method('getClassAnnotation'); + + $this->loader->load(__DIR__.'/../Fixtures/AnnotatedClasses/FooClass.php'); + } + + /** + * @requires PHP 5.4 + */ + public function testLoadTraitWithClassConstant() + { + $this->reader->expects($this->never())->method('getClassAnnotation'); + + $this->loader->load(__DIR__.'/../Fixtures/AnnotatedClasses/FooTrait.php'); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage Did you forgot to add the "loader->load(__DIR__.'/../Fixtures/OtherAnnotatedClasses/NoStartTagClass.php'); + } + + /** + * @requires PHP 5.6 + */ + public function testLoadVariadic() + { + $route = new Route(array('path' => '/path/to/{id}')); + $this->reader->expects($this->once())->method('getClassAnnotation'); + $this->reader->expects($this->once())->method('getMethodAnnotations') + ->will($this->returnValue(array($route))); + + $this->loader->load(__DIR__.'/../Fixtures/OtherAnnotatedClasses/VariadicClass.php'); + } + + /** + * @requires PHP 7.0 + */ + public function testLoadAnonymousClass() + { + $this->reader->expects($this->never())->method('getClassAnnotation'); + $this->reader->expects($this->never())->method('getMethodAnnotations'); + + $this->loader->load(__DIR__.'/../Fixtures/OtherAnnotatedClasses/AnonymousClassInTrait.php'); + } + + public function testSupports() + { + $fixture = __DIR__.'/../Fixtures/annotated.php'; + + $this->assertTrue($this->loader->supports($fixture), '->supports() returns true if the resource is loadable'); + $this->assertFalse($this->loader->supports('foo.foo'), '->supports() returns true if the resource is loadable'); + + $this->assertTrue($this->loader->supports($fixture, 'annotation'), '->supports() checks the resource type if specified'); + $this->assertFalse($this->loader->supports($fixture, 'foo'), '->supports() checks the resource type if specified'); + } +} diff --git a/lib/silex/vendor/symfony/routing/Tests/Loader/ClosureLoaderTest.php b/lib/silex/vendor/symfony/routing/Tests/Loader/ClosureLoaderTest.php new file mode 100644 index 000000000..5d963f86f --- /dev/null +++ b/lib/silex/vendor/symfony/routing/Tests/Loader/ClosureLoaderTest.php @@ -0,0 +1,49 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Routing\Tests\Loader; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Routing\Loader\ClosureLoader; +use Symfony\Component\Routing\Route; +use Symfony\Component\Routing\RouteCollection; + +class ClosureLoaderTest extends TestCase +{ + public function testSupports() + { + $loader = new ClosureLoader(); + + $closure = function () {}; + + $this->assertTrue($loader->supports($closure), '->supports() returns true if the resource is loadable'); + $this->assertFalse($loader->supports('foo.foo'), '->supports() returns true if the resource is loadable'); + + $this->assertTrue($loader->supports($closure, 'closure'), '->supports() checks the resource type if specified'); + $this->assertFalse($loader->supports($closure, 'foo'), '->supports() checks the resource type if specified'); + } + + public function testLoad() + { + $loader = new ClosureLoader(); + + $route = new Route('/'); + $routes = $loader->load(function () use ($route) { + $routes = new RouteCollection(); + + $routes->add('foo', $route); + + return $routes; + }); + + $this->assertEquals($route, $routes->get('foo'), '->load() loads a \Closure resource'); + } +} diff --git a/lib/silex/vendor/symfony/routing/Tests/Loader/DirectoryLoaderTest.php b/lib/silex/vendor/symfony/routing/Tests/Loader/DirectoryLoaderTest.php new file mode 100644 index 000000000..fc29d371e --- /dev/null +++ b/lib/silex/vendor/symfony/routing/Tests/Loader/DirectoryLoaderTest.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\Routing\Tests\Loader; + +use Symfony\Component\Routing\Loader\DirectoryLoader; +use Symfony\Component\Routing\Loader\YamlFileLoader; +use Symfony\Component\Routing\Loader\AnnotationFileLoader; +use Symfony\Component\Config\Loader\LoaderResolver; +use Symfony\Component\Config\FileLocator; +use Symfony\Component\Routing\RouteCollection; + +class DirectoryLoaderTest extends AbstractAnnotationLoaderTest +{ + private $loader; + private $reader; + + protected function setUp() + { + parent::setUp(); + + $locator = new FileLocator(); + $this->reader = $this->getReader(); + $this->loader = new DirectoryLoader($locator); + $resolver = new LoaderResolver(array( + new YamlFileLoader($locator), + new AnnotationFileLoader($locator, $this->getClassLoader($this->reader)), + $this->loader, + )); + $this->loader->setResolver($resolver); + } + + public function testLoadDirectory() + { + $collection = $this->loader->load(__DIR__.'/../Fixtures/directory', 'directory'); + $this->verifyCollection($collection); + } + + public function testImportDirectory() + { + $collection = $this->loader->load(__DIR__.'/../Fixtures/directory_import', 'directory'); + $this->verifyCollection($collection); + } + + private function verifyCollection(RouteCollection $collection) + { + $routes = $collection->all(); + + $this->assertCount(3, $routes, 'Three routes are loaded'); + $this->assertContainsOnly('Symfony\Component\Routing\Route', $routes); + + for ($i = 1; $i <= 3; ++$i) { + $this->assertSame('/route/'.$i, $routes['route'.$i]->getPath()); + } + } + + public function testSupports() + { + $fixturesDir = __DIR__.'/../Fixtures'; + + $this->assertFalse($this->loader->supports($fixturesDir), '->supports(*) returns false'); + + $this->assertTrue($this->loader->supports($fixturesDir, 'directory'), '->supports(*, "directory") returns true'); + $this->assertFalse($this->loader->supports($fixturesDir, 'foo'), '->supports(*, "foo") returns false'); + } +} diff --git a/lib/silex/vendor/symfony/routing/Tests/Loader/ObjectRouteLoaderTest.php b/lib/silex/vendor/symfony/routing/Tests/Loader/ObjectRouteLoaderTest.php new file mode 100644 index 000000000..408fa0b45 --- /dev/null +++ b/lib/silex/vendor/symfony/routing/Tests/Loader/ObjectRouteLoaderTest.php @@ -0,0 +1,123 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Routing\Tests\Loader; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Routing\Loader\ObjectRouteLoader; +use Symfony\Component\Routing\Route; +use Symfony\Component\Routing\RouteCollection; + +class ObjectRouteLoaderTest extends TestCase +{ + public function testLoadCallsServiceAndReturnsCollection() + { + $loader = new ObjectRouteLoaderForTest(); + + // create a basic collection that will be returned + $collection = new RouteCollection(); + $collection->add('foo', new Route('/foo')); + + $loader->loaderMap = array( + 'my_route_provider_service' => new RouteService($collection), + ); + + $actualRoutes = $loader->load( + 'my_route_provider_service:loadRoutes', + 'service' + ); + + $this->assertSame($collection, $actualRoutes); + // the service file should be listed as a resource + $this->assertNotEmpty($actualRoutes->getResources()); + } + + /** + * @expectedException \InvalidArgumentException + * @dataProvider getBadResourceStrings + */ + public function testExceptionWithoutSyntax($resourceString) + { + $loader = new ObjectRouteLoaderForTest(); + $loader->load($resourceString); + } + + public function getBadResourceStrings() + { + return array( + array('Foo'), + array('Bar::baz'), + array('Foo:Bar:baz'), + ); + } + + /** + * @expectedException \LogicException + */ + public function testExceptionOnNoObjectReturned() + { + $loader = new ObjectRouteLoaderForTest(); + $loader->loaderMap = array('my_service' => 'NOT_AN_OBJECT'); + $loader->load('my_service:method'); + } + + /** + * @expectedException \BadMethodCallException + */ + public function testExceptionOnBadMethod() + { + $loader = new ObjectRouteLoaderForTest(); + $loader->loaderMap = array('my_service' => new \stdClass()); + $loader->load('my_service:method'); + } + + /** + * @expectedException \LogicException + */ + public function testExceptionOnMethodNotReturningCollection() + { + $service = $this->getMockBuilder('stdClass') + ->setMethods(array('loadRoutes')) + ->getMock(); + $service->expects($this->once()) + ->method('loadRoutes') + ->will($this->returnValue('NOT_A_COLLECTION')); + + $loader = new ObjectRouteLoaderForTest(); + $loader->loaderMap = array('my_service' => $service); + $loader->load('my_service:loadRoutes'); + } +} + +class ObjectRouteLoaderForTest extends ObjectRouteLoader +{ + public $loaderMap = array(); + + protected function getServiceObject($id) + { + return isset($this->loaderMap[$id]) ? $this->loaderMap[$id] : null; + } +} + +class RouteService +{ + private $collection; + + public function __construct($collection) + { + $this->collection = $collection; + } + + public function loadRoutes() + { + return $this->collection; + } +} diff --git a/lib/silex/vendor/symfony/routing/Tests/Loader/PhpFileLoaderTest.php b/lib/silex/vendor/symfony/routing/Tests/Loader/PhpFileLoaderTest.php new file mode 100644 index 000000000..608d84ed7 --- /dev/null +++ b/lib/silex/vendor/symfony/routing/Tests/Loader/PhpFileLoaderTest.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\Routing\Tests\Loader; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Config\FileLocator; +use Symfony\Component\Config\Resource\FileResource; +use Symfony\Component\Routing\Loader\PhpFileLoader; +use Symfony\Component\Routing\Route; +use Symfony\Component\Routing\RouteCollection; + +class PhpFileLoaderTest extends TestCase +{ + public function testSupports() + { + $loader = new PhpFileLoader($this->getMockBuilder('Symfony\Component\Config\FileLocator')->getMock()); + + $this->assertTrue($loader->supports('foo.php'), '->supports() returns true if the resource is loadable'); + $this->assertFalse($loader->supports('foo.foo'), '->supports() returns true if the resource is loadable'); + + $this->assertTrue($loader->supports('foo.php', 'php'), '->supports() checks the resource type if specified'); + $this->assertFalse($loader->supports('foo.php', 'foo'), '->supports() checks the resource type if specified'); + } + + public function testLoadWithRoute() + { + $loader = new PhpFileLoader(new FileLocator(array(__DIR__.'/../Fixtures'))); + $routeCollection = $loader->load('validpattern.php'); + $routes = $routeCollection->all(); + + $this->assertCount(1, $routes, 'One route is loaded'); + $this->assertContainsOnly('Symfony\Component\Routing\Route', $routes); + + foreach ($routes as $route) { + $this->assertSame('/blog/{slug}', $route->getPath()); + $this->assertSame('MyBlogBundle:Blog:show', $route->getDefault('_controller')); + $this->assertSame('{locale}.example.com', $route->getHost()); + $this->assertSame('RouteCompiler', $route->getOption('compiler_class')); + $this->assertEquals(array('GET', 'POST', 'PUT', 'OPTIONS'), $route->getMethods()); + $this->assertEquals(array('https'), $route->getSchemes()); + } + } + + public function testLoadWithImport() + { + $loader = new PhpFileLoader(new FileLocator(array(__DIR__.'/../Fixtures'))); + $routeCollection = $loader->load('validresource.php'); + $routes = $routeCollection->all(); + + $this->assertCount(1, $routes, 'One route is loaded'); + $this->assertContainsOnly('Symfony\Component\Routing\Route', $routes); + + foreach ($routes as $route) { + $this->assertSame('/prefix/blog/{slug}', $route->getPath()); + $this->assertSame('MyBlogBundle:Blog:show', $route->getDefault('_controller')); + $this->assertSame('{locale}.example.com', $route->getHost()); + $this->assertSame('RouteCompiler', $route->getOption('compiler_class')); + $this->assertEquals(array('GET', 'POST', 'PUT', 'OPTIONS'), $route->getMethods()); + $this->assertEquals(array('https'), $route->getSchemes()); + } + } + + public function testThatDefiningVariableInConfigFileHasNoSideEffects() + { + $locator = new FileLocator(array(__DIR__.'/../Fixtures')); + $loader = new PhpFileLoader($locator); + $routeCollection = $loader->load('with_define_path_variable.php'); + $resources = $routeCollection->getResources(); + $this->assertCount(1, $resources); + $this->assertContainsOnly('Symfony\Component\Config\Resource\ResourceInterface', $resources); + $fileResource = reset($resources); + $this->assertSame( + realpath($locator->locate('with_define_path_variable.php')), + (string) $fileResource + ); + } + + public function testRoutingConfigurator() + { + $locator = new FileLocator(array(__DIR__.'/../Fixtures')); + $loader = new PhpFileLoader($locator); + $routeCollection = $loader->load('php_dsl.php'); + + $expectedCollection = new RouteCollection(); + + $expectedCollection->add('foo', (new Route('/foo')) + ->setOptions(array('utf8' => true)) + ->setCondition('abc') + ); + $expectedCollection->add('buz', (new Route('/zub')) + ->setDefaults(array('_controller' => 'foo:act')) + ); + $expectedCollection->add('c_bar', (new Route('/sub/pub/bar')) + ->setRequirements(array('id' => '\d+')) + ); + $expectedCollection->add('c_pub_buz', (new Route('/sub/pub/buz')) + ->setHost('host') + ->setRequirements(array('id' => '\d+')) + ); + $expectedCollection->add('ouf', (new Route('/ouf')) + ->setSchemes(array('https')) + ->setMethods(array('GET')) + ->setDefaults(array('id' => 0)) + ); + + $expectedCollection->addResource(new FileResource(realpath(__DIR__.'/../Fixtures/php_dsl_sub.php'))); + $expectedCollection->addResource(new FileResource(realpath(__DIR__.'/../Fixtures/php_dsl.php'))); + + $this->assertEquals($expectedCollection, $routeCollection); + } +} diff --git a/lib/silex/vendor/symfony/routing/Tests/Loader/XmlFileLoaderTest.php b/lib/silex/vendor/symfony/routing/Tests/Loader/XmlFileLoaderTest.php new file mode 100644 index 000000000..221434b00 --- /dev/null +++ b/lib/silex/vendor/symfony/routing/Tests/Loader/XmlFileLoaderTest.php @@ -0,0 +1,364 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Routing\Tests\Loader; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Config\FileLocator; +use Symfony\Component\Routing\Loader\XmlFileLoader; +use Symfony\Component\Routing\Tests\Fixtures\CustomXmlFileLoader; + +class XmlFileLoaderTest extends TestCase +{ + public function testSupports() + { + $loader = new XmlFileLoader($this->getMockBuilder('Symfony\Component\Config\FileLocator')->getMock()); + + $this->assertTrue($loader->supports('foo.xml'), '->supports() returns true if the resource is loadable'); + $this->assertFalse($loader->supports('foo.foo'), '->supports() returns true if the resource is loadable'); + + $this->assertTrue($loader->supports('foo.xml', 'xml'), '->supports() checks the resource type if specified'); + $this->assertFalse($loader->supports('foo.xml', 'foo'), '->supports() checks the resource type if specified'); + } + + public function testLoadWithRoute() + { + $loader = new XmlFileLoader(new FileLocator(array(__DIR__.'/../Fixtures'))); + $routeCollection = $loader->load('validpattern.xml'); + $route = $routeCollection->get('blog_show'); + + $this->assertInstanceOf('Symfony\Component\Routing\Route', $route); + $this->assertSame('/blog/{slug}', $route->getPath()); + $this->assertSame('{locale}.example.com', $route->getHost()); + $this->assertSame('MyBundle:Blog:show', $route->getDefault('_controller')); + $this->assertSame('\w+', $route->getRequirement('locale')); + $this->assertSame('RouteCompiler', $route->getOption('compiler_class')); + $this->assertEquals(array('GET', 'POST', 'PUT', 'OPTIONS'), $route->getMethods()); + $this->assertEquals(array('https'), $route->getSchemes()); + $this->assertEquals('context.getMethod() == "GET"', $route->getCondition()); + } + + public function testLoadWithNamespacePrefix() + { + $loader = new XmlFileLoader(new FileLocator(array(__DIR__.'/../Fixtures'))); + $routeCollection = $loader->load('namespaceprefix.xml'); + + $this->assertCount(1, $routeCollection->all(), 'One route is loaded'); + + $route = $routeCollection->get('blog_show'); + $this->assertSame('/blog/{slug}', $route->getPath()); + $this->assertSame('{_locale}.example.com', $route->getHost()); + $this->assertSame('MyBundle:Blog:show', $route->getDefault('_controller')); + $this->assertSame('\w+', $route->getRequirement('slug')); + $this->assertSame('en|fr|de', $route->getRequirement('_locale')); + $this->assertNull($route->getDefault('slug')); + $this->assertSame('RouteCompiler', $route->getOption('compiler_class')); + $this->assertSame(1, $route->getDefault('page')); + } + + public function testLoadWithImport() + { + $loader = new XmlFileLoader(new FileLocator(array(__DIR__.'/../Fixtures'))); + $routeCollection = $loader->load('validresource.xml'); + $routes = $routeCollection->all(); + + $this->assertCount(2, $routes, 'Two routes are loaded'); + $this->assertContainsOnly('Symfony\Component\Routing\Route', $routes); + + foreach ($routes as $route) { + $this->assertSame('/{foo}/blog/{slug}', $route->getPath()); + $this->assertSame('123', $route->getDefault('foo')); + $this->assertSame('\d+', $route->getRequirement('foo')); + $this->assertSame('bar', $route->getOption('foo')); + $this->assertSame('', $route->getHost()); + $this->assertSame('context.getMethod() == "POST"', $route->getCondition()); + } + } + + /** + * @expectedException \InvalidArgumentException + * @dataProvider getPathsToInvalidFiles + */ + public function testLoadThrowsExceptionWithInvalidFile($filePath) + { + $loader = new XmlFileLoader(new FileLocator(array(__DIR__.'/../Fixtures'))); + $loader->load($filePath); + } + + /** + * @expectedException \InvalidArgumentException + * @dataProvider getPathsToInvalidFiles + */ + public function testLoadThrowsExceptionWithInvalidFileEvenWithoutSchemaValidation($filePath) + { + $loader = new CustomXmlFileLoader(new FileLocator(array(__DIR__.'/../Fixtures'))); + $loader->load($filePath); + } + + public function getPathsToInvalidFiles() + { + return array(array('nonvalidnode.xml'), array('nonvalidroute.xml'), array('nonvalid.xml'), array('missing_id.xml'), array('missing_path.xml')); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage Document types are not allowed. + */ + public function testDocTypeIsNotAllowed() + { + $loader = new XmlFileLoader(new FileLocator(array(__DIR__.'/../Fixtures'))); + $loader->load('withdoctype.xml'); + } + + public function testNullValues() + { + $loader = new XmlFileLoader(new FileLocator(array(__DIR__.'/../Fixtures'))); + $routeCollection = $loader->load('null_values.xml'); + $route = $routeCollection->get('blog_show'); + + $this->assertTrue($route->hasDefault('foo')); + $this->assertNull($route->getDefault('foo')); + $this->assertTrue($route->hasDefault('bar')); + $this->assertNull($route->getDefault('bar')); + $this->assertEquals('foo', $route->getDefault('foobar')); + $this->assertEquals('bar', $route->getDefault('baz')); + } + + public function testScalarDataTypeDefaults() + { + $loader = new XmlFileLoader(new FileLocator(array(__DIR__.'/../Fixtures'))); + $routeCollection = $loader->load('scalar_defaults.xml'); + $route = $routeCollection->get('blog'); + + $this->assertSame( + array( + '_controller' => 'AcmeBlogBundle:Blog:index', + 'slug' => null, + 'published' => true, + 'page' => 1, + 'price' => 3.5, + 'archived' => false, + 'free' => true, + 'locked' => false, + 'foo' => null, + 'bar' => null, + ), + $route->getDefaults() + ); + } + + public function testListDefaults() + { + $loader = new XmlFileLoader(new FileLocator(array(__DIR__.'/../Fixtures'))); + $routeCollection = $loader->load('list_defaults.xml'); + $route = $routeCollection->get('blog'); + + $this->assertSame( + array( + '_controller' => 'AcmeBlogBundle:Blog:index', + 'values' => array(true, 1, 3.5, 'foo'), + ), + $route->getDefaults() + ); + } + + public function testListInListDefaults() + { + $loader = new XmlFileLoader(new FileLocator(array(__DIR__.'/../Fixtures'))); + $routeCollection = $loader->load('list_in_list_defaults.xml'); + $route = $routeCollection->get('blog'); + + $this->assertSame( + array( + '_controller' => 'AcmeBlogBundle:Blog:index', + 'values' => array(array(true, 1, 3.5, 'foo')), + ), + $route->getDefaults() + ); + } + + public function testListInMapDefaults() + { + $loader = new XmlFileLoader(new FileLocator(array(__DIR__.'/../Fixtures'))); + $routeCollection = $loader->load('list_in_map_defaults.xml'); + $route = $routeCollection->get('blog'); + + $this->assertSame( + array( + '_controller' => 'AcmeBlogBundle:Blog:index', + 'values' => array('list' => array(true, 1, 3.5, 'foo')), + ), + $route->getDefaults() + ); + } + + public function testMapDefaults() + { + $loader = new XmlFileLoader(new FileLocator(array(__DIR__.'/../Fixtures'))); + $routeCollection = $loader->load('map_defaults.xml'); + $route = $routeCollection->get('blog'); + + $this->assertSame( + array( + '_controller' => 'AcmeBlogBundle:Blog:index', + 'values' => array( + 'public' => true, + 'page' => 1, + 'price' => 3.5, + 'title' => 'foo', + ), + ), + $route->getDefaults() + ); + } + + public function testMapInListDefaults() + { + $loader = new XmlFileLoader(new FileLocator(array(__DIR__.'/../Fixtures'))); + $routeCollection = $loader->load('map_in_list_defaults.xml'); + $route = $routeCollection->get('blog'); + + $this->assertSame( + array( + '_controller' => 'AcmeBlogBundle:Blog:index', + 'values' => array(array( + 'public' => true, + 'page' => 1, + 'price' => 3.5, + 'title' => 'foo', + )), + ), + $route->getDefaults() + ); + } + + public function testMapInMapDefaults() + { + $loader = new XmlFileLoader(new FileLocator(array(__DIR__.'/../Fixtures'))); + $routeCollection = $loader->load('map_in_map_defaults.xml'); + $route = $routeCollection->get('blog'); + + $this->assertSame( + array( + '_controller' => 'AcmeBlogBundle:Blog:index', + 'values' => array('map' => array( + 'public' => true, + 'page' => 1, + 'price' => 3.5, + 'title' => 'foo', + )), + ), + $route->getDefaults() + ); + } + + public function testNullValuesInList() + { + $loader = new XmlFileLoader(new FileLocator(array(__DIR__.'/../Fixtures'))); + $routeCollection = $loader->load('list_null_values.xml'); + $route = $routeCollection->get('blog'); + + $this->assertSame(array(null, null, null, null, null, null), $route->getDefault('list')); + } + + public function testNullValuesInMap() + { + $loader = new XmlFileLoader(new FileLocator(array(__DIR__.'/../Fixtures'))); + $routeCollection = $loader->load('map_null_values.xml'); + $route = $routeCollection->get('blog'); + + $this->assertSame( + array( + 'boolean' => null, + 'integer' => null, + 'float' => null, + 'string' => null, + 'list' => null, + 'map' => null, + ), + $route->getDefault('map') + ); + } + + public function testLoadRouteWithControllerAttribute() + { + $loader = new XmlFileLoader(new FileLocator(array(__DIR__.'/../Fixtures/controller'))); + $routeCollection = $loader->load('routing.xml'); + + $route = $routeCollection->get('app_homepage'); + + $this->assertSame('AppBundle:Homepage:show', $route->getDefault('_controller')); + } + + public function testLoadRouteWithoutControllerAttribute() + { + $loader = new XmlFileLoader(new FileLocator(array(__DIR__.'/../Fixtures/controller'))); + $routeCollection = $loader->load('routing.xml'); + + $route = $routeCollection->get('app_logout'); + + $this->assertNull($route->getDefault('_controller')); + } + + public function testLoadRouteWithControllerSetInDefaults() + { + $loader = new XmlFileLoader(new FileLocator(array(__DIR__.'/../Fixtures/controller'))); + $routeCollection = $loader->load('routing.xml'); + + $route = $routeCollection->get('app_blog'); + + $this->assertSame('AppBundle:Blog:list', $route->getDefault('_controller')); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessageRegExp /The routing file "[^"]*" must not specify both the "controller" attribute and the defaults key "_controller" for "app_blog"/ + */ + public function testOverrideControllerInDefaults() + { + $loader = new XmlFileLoader(new FileLocator(array(__DIR__.'/../Fixtures/controller'))); + $loader->load('override_defaults.xml'); + } + + /** + * @dataProvider provideFilesImportingRoutesWithControllers + */ + public function testImportRouteWithController($file) + { + $loader = new XmlFileLoader(new FileLocator(array(__DIR__.'/../Fixtures/controller'))); + $routeCollection = $loader->load($file); + + $route = $routeCollection->get('app_homepage'); + $this->assertSame('FrameworkBundle:Template:template', $route->getDefault('_controller')); + + $route = $routeCollection->get('app_blog'); + $this->assertSame('FrameworkBundle:Template:template', $route->getDefault('_controller')); + + $route = $routeCollection->get('app_logout'); + $this->assertSame('FrameworkBundle:Template:template', $route->getDefault('_controller')); + } + + public function provideFilesImportingRoutesWithControllers() + { + yield array('import_controller.xml'); + yield array('import__controller.xml'); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessageRegExp /The routing file "[^"]*" must not specify both the "controller" attribute and the defaults key "_controller" for the "import" tag/ + */ + public function testImportWithOverriddenController() + { + $loader = new XmlFileLoader(new FileLocator(array(__DIR__.'/../Fixtures/controller'))); + $loader->load('import_override_defaults.xml'); + } +} diff --git a/lib/silex/vendor/symfony/routing/Tests/Loader/YamlFileLoaderTest.php b/lib/silex/vendor/symfony/routing/Tests/Loader/YamlFileLoaderTest.php new file mode 100644 index 000000000..1f7fd4389 --- /dev/null +++ b/lib/silex/vendor/symfony/routing/Tests/Loader/YamlFileLoaderTest.php @@ -0,0 +1,185 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Routing\Tests\Loader; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Config\FileLocator; +use Symfony\Component\Routing\Loader\YamlFileLoader; +use Symfony\Component\Config\Resource\FileResource; + +class YamlFileLoaderTest extends TestCase +{ + public function testSupports() + { + $loader = new YamlFileLoader($this->getMockBuilder('Symfony\Component\Config\FileLocator')->getMock()); + + $this->assertTrue($loader->supports('foo.yml'), '->supports() returns true if the resource is loadable'); + $this->assertTrue($loader->supports('foo.yaml'), '->supports() returns true if the resource is loadable'); + $this->assertFalse($loader->supports('foo.foo'), '->supports() returns true if the resource is loadable'); + + $this->assertTrue($loader->supports('foo.yml', 'yaml'), '->supports() checks the resource type if specified'); + $this->assertTrue($loader->supports('foo.yaml', 'yaml'), '->supports() checks the resource type if specified'); + $this->assertFalse($loader->supports('foo.yml', 'foo'), '->supports() checks the resource type if specified'); + } + + public function testLoadDoesNothingIfEmpty() + { + $loader = new YamlFileLoader(new FileLocator(array(__DIR__.'/../Fixtures'))); + $collection = $loader->load('empty.yml'); + + $this->assertEquals(array(), $collection->all()); + $this->assertEquals(array(new FileResource(realpath(__DIR__.'/../Fixtures/empty.yml'))), $collection->getResources()); + } + + /** + * @expectedException \InvalidArgumentException + * @dataProvider getPathsToInvalidFiles + */ + public function testLoadThrowsExceptionWithInvalidFile($filePath) + { + $loader = new YamlFileLoader(new FileLocator(array(__DIR__.'/../Fixtures'))); + $loader->load($filePath); + } + + public function getPathsToInvalidFiles() + { + return array( + array('nonvalid.yml'), + array('nonvalid2.yml'), + array('incomplete.yml'), + array('nonvalidkeys.yml'), + array('nonesense_resource_plus_path.yml'), + array('nonesense_type_without_resource.yml'), + array('bad_format.yml'), + ); + } + + public function testLoadSpecialRouteName() + { + $loader = new YamlFileLoader(new FileLocator(array(__DIR__.'/../Fixtures'))); + $routeCollection = $loader->load('special_route_name.yml'); + $route = $routeCollection->get('#$péß^a|'); + + $this->assertInstanceOf('Symfony\Component\Routing\Route', $route); + $this->assertSame('/true', $route->getPath()); + } + + public function testLoadWithRoute() + { + $loader = new YamlFileLoader(new FileLocator(array(__DIR__.'/../Fixtures'))); + $routeCollection = $loader->load('validpattern.yml'); + $route = $routeCollection->get('blog_show'); + + $this->assertInstanceOf('Symfony\Component\Routing\Route', $route); + $this->assertSame('/blog/{slug}', $route->getPath()); + $this->assertSame('{locale}.example.com', $route->getHost()); + $this->assertSame('MyBundle:Blog:show', $route->getDefault('_controller')); + $this->assertSame('\w+', $route->getRequirement('locale')); + $this->assertSame('RouteCompiler', $route->getOption('compiler_class')); + $this->assertEquals(array('GET', 'POST', 'PUT', 'OPTIONS'), $route->getMethods()); + $this->assertEquals(array('https'), $route->getSchemes()); + $this->assertEquals('context.getMethod() == "GET"', $route->getCondition()); + } + + public function testLoadWithResource() + { + $loader = new YamlFileLoader(new FileLocator(array(__DIR__.'/../Fixtures'))); + $routeCollection = $loader->load('validresource.yml'); + $routes = $routeCollection->all(); + + $this->assertCount(2, $routes, 'Two routes are loaded'); + $this->assertContainsOnly('Symfony\Component\Routing\Route', $routes); + + foreach ($routes as $route) { + $this->assertSame('/{foo}/blog/{slug}', $route->getPath()); + $this->assertSame('123', $route->getDefault('foo')); + $this->assertSame('\d+', $route->getRequirement('foo')); + $this->assertSame('bar', $route->getOption('foo')); + $this->assertSame('', $route->getHost()); + $this->assertSame('context.getMethod() == "POST"', $route->getCondition()); + } + } + + public function testLoadRouteWithControllerAttribute() + { + $loader = new YamlFileLoader(new FileLocator(array(__DIR__.'/../Fixtures/controller'))); + $routeCollection = $loader->load('routing.yml'); + + $route = $routeCollection->get('app_homepage'); + + $this->assertSame('AppBundle:Homepage:show', $route->getDefault('_controller')); + } + + public function testLoadRouteWithoutControllerAttribute() + { + $loader = new YamlFileLoader(new FileLocator(array(__DIR__.'/../Fixtures/controller'))); + $routeCollection = $loader->load('routing.yml'); + + $route = $routeCollection->get('app_logout'); + + $this->assertNull($route->getDefault('_controller')); + } + + public function testLoadRouteWithControllerSetInDefaults() + { + $loader = new YamlFileLoader(new FileLocator(array(__DIR__.'/../Fixtures/controller'))); + $routeCollection = $loader->load('routing.yml'); + + $route = $routeCollection->get('app_blog'); + + $this->assertSame('AppBundle:Blog:list', $route->getDefault('_controller')); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessageRegExp /The routing file "[^"]*" must not specify both the "controller" key and the defaults key "_controller" for "app_blog"/ + */ + public function testOverrideControllerInDefaults() + { + $loader = new YamlFileLoader(new FileLocator(array(__DIR__.'/../Fixtures/controller'))); + $loader->load('override_defaults.yml'); + } + + /** + * @dataProvider provideFilesImportingRoutesWithControllers + */ + public function testImportRouteWithController($file) + { + $loader = new YamlFileLoader(new FileLocator(array(__DIR__.'/../Fixtures/controller'))); + $routeCollection = $loader->load($file); + + $route = $routeCollection->get('app_homepage'); + $this->assertSame('FrameworkBundle:Template:template', $route->getDefault('_controller')); + + $route = $routeCollection->get('app_blog'); + $this->assertSame('FrameworkBundle:Template:template', $route->getDefault('_controller')); + + $route = $routeCollection->get('app_logout'); + $this->assertSame('FrameworkBundle:Template:template', $route->getDefault('_controller')); + } + + public function provideFilesImportingRoutesWithControllers() + { + yield array('import_controller.yml'); + yield array('import__controller.yml'); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessageRegExp /The routing file "[^"]*" must not specify both the "controller" key and the defaults key "_controller" for "_static"/ + */ + public function testImportWithOverriddenController() + { + $loader = new YamlFileLoader(new FileLocator(array(__DIR__.'/../Fixtures/controller'))); + $loader->load('import_override_defaults.yml'); + } +} diff --git a/lib/silex/vendor/symfony/routing/Tests/Matcher/Dumper/DumperCollectionTest.php b/lib/silex/vendor/symfony/routing/Tests/Matcher/Dumper/DumperCollectionTest.php new file mode 100644 index 000000000..823efdb84 --- /dev/null +++ b/lib/silex/vendor/symfony/routing/Tests/Matcher/Dumper/DumperCollectionTest.php @@ -0,0 +1,34 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Routing\Tests\Matcher\Dumper; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Routing\Matcher\Dumper\DumperCollection; + +class DumperCollectionTest extends TestCase +{ + public function testGetRoot() + { + $a = new DumperCollection(); + + $b = new DumperCollection(); + $a->add($b); + + $c = new DumperCollection(); + $b->add($c); + + $d = new DumperCollection(); + $c->add($d); + + $this->assertSame($a, $c->getRoot()); + } +} diff --git a/lib/silex/vendor/symfony/routing/Tests/Matcher/Dumper/PhpMatcherDumperTest.php b/lib/silex/vendor/symfony/routing/Tests/Matcher/Dumper/PhpMatcherDumperTest.php new file mode 100644 index 000000000..e4c18c47b --- /dev/null +++ b/lib/silex/vendor/symfony/routing/Tests/Matcher/Dumper/PhpMatcherDumperTest.php @@ -0,0 +1,459 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Routing\Tests\Matcher\Dumper; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Routing\Matcher\Dumper\PhpMatcherDumper; +use Symfony\Component\Routing\Matcher\RedirectableUrlMatcherInterface; +use Symfony\Component\Routing\Matcher\UrlMatcher; +use Symfony\Component\Routing\RequestContext; +use Symfony\Component\Routing\Route; +use Symfony\Component\Routing\RouteCollection; + +class PhpMatcherDumperTest extends TestCase +{ + /** + * @var string + */ + private $matcherClass; + + /** + * @var string + */ + private $dumpPath; + + protected function setUp() + { + parent::setUp(); + + $this->matcherClass = uniqid('ProjectUrlMatcher'); + $this->dumpPath = sys_get_temp_dir().DIRECTORY_SEPARATOR.'php_matcher.'.$this->matcherClass.'.php'; + } + + protected function tearDown() + { + parent::tearDown(); + + @unlink($this->dumpPath); + } + + /** + * @expectedException \LogicException + */ + public function testDumpWhenSchemeIsUsedWithoutAProperDumper() + { + $collection = new RouteCollection(); + $collection->add('secure', new Route( + '/secure', + array(), + array(), + array(), + '', + array('https') + )); + $dumper = new PhpMatcherDumper($collection); + $dumper->dump(); + } + + public function testRedirectPreservesUrlEncoding() + { + $collection = new RouteCollection(); + $collection->add('foo', new Route('/foo:bar/')); + + $class = $this->generateDumpedMatcher($collection, true); + + $matcher = $this->getMockBuilder($class) + ->setMethods(array('redirect')) + ->setConstructorArgs(array(new RequestContext())) + ->getMock(); + + $matcher->expects($this->once())->method('redirect')->with('/foo%3Abar/', 'foo')->willReturn(array()); + + $matcher->match('/foo%3Abar'); + } + + /** + * @dataProvider getRouteCollections + */ + public function testDump(RouteCollection $collection, $fixture, $options = array()) + { + $basePath = __DIR__.'/../../Fixtures/dumper/'; + + $dumper = new PhpMatcherDumper($collection); + $this->assertStringEqualsFile($basePath.$fixture, $dumper->dump($options), '->dump() correctly dumps routes as optimized PHP code.'); + } + + public function getRouteCollections() + { + /* test case 1 */ + + $collection = new RouteCollection(); + + $collection->add('overridden', new Route('/overridden')); + + // defaults and requirements + $collection->add('foo', new Route( + '/foo/{bar}', + array('def' => 'test'), + array('bar' => 'baz|symfony') + )); + // method requirement + $collection->add('bar', new Route( + '/bar/{foo}', + array(), + array(), + array(), + '', + array(), + array('GET', 'head') + )); + // GET method requirement automatically adds HEAD as valid + $collection->add('barhead', new Route( + '/barhead/{foo}', + array(), + array(), + array(), + '', + array(), + array('GET') + )); + // simple + $collection->add('baz', new Route( + '/test/baz' + )); + // simple with extension + $collection->add('baz2', new Route( + '/test/baz.html' + )); + // trailing slash + $collection->add('baz3', new Route( + '/test/baz3/' + )); + // trailing slash with variable + $collection->add('baz4', new Route( + '/test/{foo}/' + )); + // trailing slash and method + $collection->add('baz5', new Route( + '/test/{foo}/', + array(), + array(), + array(), + '', + array(), + array('post') + )); + // complex name + $collection->add('baz.baz6', new Route( + '/test/{foo}/', + array(), + array(), + array(), + '', + array(), + array('put') + )); + // defaults without variable + $collection->add('foofoo', new Route( + '/foofoo', + array('def' => 'test') + )); + // pattern with quotes + $collection->add('quoter', new Route( + '/{quoter}', + array(), + array('quoter' => '[\']+') + )); + // space in pattern + $collection->add('space', new Route( + '/spa ce' + )); + + // prefixes + $collection1 = new RouteCollection(); + $collection1->add('overridden', new Route('/overridden1')); + $collection1->add('foo1', new Route('/{foo}')); + $collection1->add('bar1', new Route('/{bar}')); + $collection1->addPrefix('/b\'b'); + $collection2 = new RouteCollection(); + $collection2->addCollection($collection1); + $collection2->add('overridden', new Route('/{var}', array(), array('var' => '.*'))); + $collection1 = new RouteCollection(); + $collection1->add('foo2', new Route('/{foo1}')); + $collection1->add('bar2', new Route('/{bar1}')); + $collection1->addPrefix('/b\'b'); + $collection2->addCollection($collection1); + $collection2->addPrefix('/a'); + $collection->addCollection($collection2); + + // overridden through addCollection() and multiple sub-collections with no own prefix + $collection1 = new RouteCollection(); + $collection1->add('overridden2', new Route('/old')); + $collection1->add('helloWorld', new Route('/hello/{who}', array('who' => 'World!'))); + $collection2 = new RouteCollection(); + $collection3 = new RouteCollection(); + $collection3->add('overridden2', new Route('/new')); + $collection3->add('hey', new Route('/hey/')); + $collection2->addCollection($collection3); + $collection1->addCollection($collection2); + $collection1->addPrefix('/multi'); + $collection->addCollection($collection1); + + // "dynamic" prefix + $collection1 = new RouteCollection(); + $collection1->add('foo3', new Route('/{foo}')); + $collection1->add('bar3', new Route('/{bar}')); + $collection1->addPrefix('/b'); + $collection1->addPrefix('{_locale}'); + $collection->addCollection($collection1); + + // route between collections + $collection->add('ababa', new Route('/ababa')); + + // collection with static prefix but only one route + $collection1 = new RouteCollection(); + $collection1->add('foo4', new Route('/{foo}')); + $collection1->addPrefix('/aba'); + $collection->addCollection($collection1); + + // prefix and host + + $collection1 = new RouteCollection(); + + $route1 = new Route('/route1', array(), array(), array(), 'a.example.com'); + $collection1->add('route1', $route1); + + $route2 = new Route('/c2/route2', array(), array(), array(), 'a.example.com'); + $collection1->add('route2', $route2); + + $route3 = new Route('/c2/route3', array(), array(), array(), 'b.example.com'); + $collection1->add('route3', $route3); + + $route4 = new Route('/route4', array(), array(), array(), 'a.example.com'); + $collection1->add('route4', $route4); + + $route5 = new Route('/route5', array(), array(), array(), 'c.example.com'); + $collection1->add('route5', $route5); + + $route6 = new Route('/route6', array(), array(), array(), null); + $collection1->add('route6', $route6); + + $collection->addCollection($collection1); + + // host and variables + + $collection1 = new RouteCollection(); + + $route11 = new Route('/route11', array(), array(), array(), '{var1}.example.com'); + $collection1->add('route11', $route11); + + $route12 = new Route('/route12', array('var1' => 'val'), array(), array(), '{var1}.example.com'); + $collection1->add('route12', $route12); + + $route13 = new Route('/route13/{name}', array(), array(), array(), '{var1}.example.com'); + $collection1->add('route13', $route13); + + $route14 = new Route('/route14/{name}', array('var1' => 'val'), array(), array(), '{var1}.example.com'); + $collection1->add('route14', $route14); + + $route15 = new Route('/route15/{name}', array(), array(), array(), 'c.example.com'); + $collection1->add('route15', $route15); + + $route16 = new Route('/route16/{name}', array('var1' => 'val'), array(), array(), null); + $collection1->add('route16', $route16); + + $route17 = new Route('/route17', array(), array(), array(), null); + $collection1->add('route17', $route17); + + $collection->addCollection($collection1); + + // multiple sub-collections with a single route and a prefix each + $collection1 = new RouteCollection(); + $collection1->add('a', new Route('/a...')); + $collection2 = new RouteCollection(); + $collection2->add('b', new Route('/{var}')); + $collection3 = new RouteCollection(); + $collection3->add('c', new Route('/{var}')); + $collection3->addPrefix('/c'); + $collection2->addCollection($collection3); + $collection2->addPrefix('/b'); + $collection1->addCollection($collection2); + $collection1->addPrefix('/a'); + $collection->addCollection($collection1); + + /* test case 2 */ + + $redirectCollection = clone $collection; + + // force HTTPS redirection + $redirectCollection->add('secure', new Route( + '/secure', + array(), + array(), + array(), + '', + array('https') + )); + + // force HTTP redirection + $redirectCollection->add('nonsecure', new Route( + '/nonsecure', + array(), + array(), + array(), + '', + array('http') + )); + + /* test case 3 */ + + $rootprefixCollection = new RouteCollection(); + $rootprefixCollection->add('static', new Route('/test')); + $rootprefixCollection->add('dynamic', new Route('/{var}')); + $rootprefixCollection->addPrefix('rootprefix'); + $route = new Route('/with-condition'); + $route->setCondition('context.getMethod() == "GET"'); + $rootprefixCollection->add('with-condition', $route); + + /* test case 4 */ + $headMatchCasesCollection = new RouteCollection(); + $headMatchCasesCollection->add('just_head', new Route( + '/just_head', + array(), + array(), + array(), + '', + array(), + array('HEAD') + )); + $headMatchCasesCollection->add('head_and_get', new Route( + '/head_and_get', + array(), + array(), + array(), + '', + array(), + array('HEAD', 'GET') + )); + $headMatchCasesCollection->add('get_and_head', new Route( + '/get_and_head', + array(), + array(), + array(), + '', + array(), + array('GET', 'HEAD') + )); + $headMatchCasesCollection->add('post_and_head', new Route( + '/post_and_get', + array(), + array(), + array(), + '', + array(), + array('POST', 'HEAD') + )); + $headMatchCasesCollection->add('put_and_post', new Route( + '/put_and_post', + array(), + array(), + array(), + '', + array(), + array('PUT', 'POST') + )); + $headMatchCasesCollection->add('put_and_get_and_head', new Route( + '/put_and_post', + array(), + array(), + array(), + '', + array(), + array('PUT', 'GET', 'HEAD') + )); + + /* test case 5 */ + $groupOptimisedCollection = new RouteCollection(); + $groupOptimisedCollection->add('a_first', new Route('/a/11')); + $groupOptimisedCollection->add('a_second', new Route('/a/22')); + $groupOptimisedCollection->add('a_third', new Route('/a/333')); + $groupOptimisedCollection->add('a_wildcard', new Route('/{param}')); + $groupOptimisedCollection->add('a_fourth', new Route('/a/44/')); + $groupOptimisedCollection->add('a_fifth', new Route('/a/55/')); + $groupOptimisedCollection->add('a_sixth', new Route('/a/66/')); + $groupOptimisedCollection->add('nested_wildcard', new Route('/nested/{param}')); + $groupOptimisedCollection->add('nested_a', new Route('/nested/group/a/')); + $groupOptimisedCollection->add('nested_b', new Route('/nested/group/b/')); + $groupOptimisedCollection->add('nested_c', new Route('/nested/group/c/')); + + $groupOptimisedCollection->add('slashed_a', new Route('/slashed/group/')); + $groupOptimisedCollection->add('slashed_b', new Route('/slashed/group/b/')); + $groupOptimisedCollection->add('slashed_c', new Route('/slashed/group/c/')); + + $trailingSlashCollection = new RouteCollection(); + $trailingSlashCollection->add('simple_trailing_slash_no_methods', new Route('/trailing/simple/no-methods/', array(), array(), array(), '', array(), array())); + $trailingSlashCollection->add('simple_trailing_slash_GET_method', new Route('/trailing/simple/get-method/', array(), array(), array(), '', array(), array('GET'))); + $trailingSlashCollection->add('simple_trailing_slash_HEAD_method', new Route('/trailing/simple/head-method/', array(), array(), array(), '', array(), array('HEAD'))); + $trailingSlashCollection->add('simple_trailing_slash_POST_method', new Route('/trailing/simple/post-method/', array(), array(), array(), '', array(), array('POST'))); + $trailingSlashCollection->add('regex_trailing_slash_no_methods', new Route('/trailing/regex/no-methods/{param}/', array(), array(), array(), '', array(), array())); + $trailingSlashCollection->add('regex_trailing_slash_GET_method', new Route('/trailing/regex/get-method/{param}/', array(), array(), array(), '', array(), array('GET'))); + $trailingSlashCollection->add('regex_trailing_slash_HEAD_method', new Route('/trailing/regex/head-method/{param}/', array(), array(), array(), '', array(), array('HEAD'))); + $trailingSlashCollection->add('regex_trailing_slash_POST_method', new Route('/trailing/regex/post-method/{param}/', array(), array(), array(), '', array(), array('POST'))); + + $trailingSlashCollection->add('simple_not_trailing_slash_no_methods', new Route('/not-trailing/simple/no-methods', array(), array(), array(), '', array(), array())); + $trailingSlashCollection->add('simple_not_trailing_slash_GET_method', new Route('/not-trailing/simple/get-method', array(), array(), array(), '', array(), array('GET'))); + $trailingSlashCollection->add('simple_not_trailing_slash_HEAD_method', new Route('/not-trailing/simple/head-method', array(), array(), array(), '', array(), array('HEAD'))); + $trailingSlashCollection->add('simple_not_trailing_slash_POST_method', new Route('/not-trailing/simple/post-method', array(), array(), array(), '', array(), array('POST'))); + $trailingSlashCollection->add('regex_not_trailing_slash_no_methods', new Route('/not-trailing/regex/no-methods/{param}', array(), array(), array(), '', array(), array())); + $trailingSlashCollection->add('regex_not_trailing_slash_GET_method', new Route('/not-trailing/regex/get-method/{param}', array(), array(), array(), '', array(), array('GET'))); + $trailingSlashCollection->add('regex_not_trailing_slash_HEAD_method', new Route('/not-trailing/regex/head-method/{param}', array(), array(), array(), '', array(), array('HEAD'))); + $trailingSlashCollection->add('regex_not_trailing_slash_POST_method', new Route('/not-trailing/regex/post-method/{param}', array(), array(), array(), '', array(), array('POST'))); + + return array( + array(new RouteCollection(), 'url_matcher0.php', array()), + array($collection, 'url_matcher1.php', array()), + array($redirectCollection, 'url_matcher2.php', array('base_class' => 'Symfony\Component\Routing\Tests\Fixtures\RedirectableUrlMatcher')), + array($rootprefixCollection, 'url_matcher3.php', array()), + array($headMatchCasesCollection, 'url_matcher4.php', array()), + array($groupOptimisedCollection, 'url_matcher5.php', array('base_class' => 'Symfony\Component\Routing\Tests\Fixtures\RedirectableUrlMatcher')), + array($trailingSlashCollection, 'url_matcher6.php', array()), + array($trailingSlashCollection, 'url_matcher7.php', array('base_class' => 'Symfony\Component\Routing\Tests\Fixtures\RedirectableUrlMatcher')), + ); + } + + /** + * @param $dumper + */ + private function generateDumpedMatcher(RouteCollection $collection, $redirectableStub = false) + { + $options = array('class' => $this->matcherClass); + + if ($redirectableStub) { + $options['base_class'] = '\Symfony\Component\Routing\Tests\Matcher\Dumper\RedirectableUrlMatcherStub'; + } + + $dumper = new PhpMatcherDumper($collection); + $code = $dumper->dump($options); + + file_put_contents($this->dumpPath, $code); + include $this->dumpPath; + + return $this->matcherClass; + } +} + +abstract class RedirectableUrlMatcherStub extends UrlMatcher implements RedirectableUrlMatcherInterface +{ + public function redirect($path, $route, $scheme = null) + { + } +} diff --git a/lib/silex/vendor/symfony/routing/Tests/Matcher/Dumper/StaticPrefixCollectionTest.php b/lib/silex/vendor/symfony/routing/Tests/Matcher/Dumper/StaticPrefixCollectionTest.php new file mode 100644 index 000000000..37419e774 --- /dev/null +++ b/lib/silex/vendor/symfony/routing/Tests/Matcher/Dumper/StaticPrefixCollectionTest.php @@ -0,0 +1,175 @@ +compile()->getStaticPrefix(); + $collection->addRoute($staticPrefix, $name); + } + + $collection->optimizeGroups(); + $dumped = $this->dumpCollection($collection); + $this->assertEquals($expected, $dumped); + } + + public function routeProvider() + { + return array( + 'Simple - not nested' => array( + array( + array('/', 'root'), + array('/prefix/segment/', 'prefix_segment'), + array('/leading/segment/', 'leading_segment'), + ), + << array( + array( + array('/', 'root'), + array('/prefix/segment/aa', 'prefix_segment'), + array('/prefix/segment/bb', 'leading_segment'), + ), + << array( + array( + array('/', 'root'), + array('/prefix/segment/', 'prefix_segment'), + array('/prefix/segment/bb', 'leading_segment'), + ), + << /prefix/segment prefix_segment +-> /prefix/segment/bb leading_segment +EOF + ), + 'Simple one level nesting' => array( + array( + array('/', 'root'), + array('/group/segment/', 'nested_segment'), + array('/group/thing/', 'some_segment'), + array('/group/other/', 'other_segment'), + ), + << /group/segment nested_segment +-> /group/thing some_segment +-> /group/other other_segment +EOF + ), + 'Retain matching order with groups' => array( + array( + array('/group/aa/', 'aa'), + array('/group/bb/', 'bb'), + array('/group/cc/', 'cc'), + array('/', 'root'), + array('/group/dd/', 'dd'), + array('/group/ee/', 'ee'), + array('/group/ff/', 'ff'), + ), + << /group/aa aa +-> /group/bb bb +-> /group/cc cc +/ root +/group +-> /group/dd dd +-> /group/ee ee +-> /group/ff ff +EOF + ), + 'Retain complex matching order with groups at base' => array( + array( + array('/aaa/111/', 'first_aaa'), + array('/prefixed/group/aa/', 'aa'), + array('/prefixed/group/bb/', 'bb'), + array('/prefixed/group/cc/', 'cc'), + array('/prefixed/', 'root'), + array('/prefixed/group/dd/', 'dd'), + array('/prefixed/group/ee/', 'ee'), + array('/prefixed/group/ff/', 'ff'), + array('/aaa/222/', 'second_aaa'), + array('/aaa/333/', 'third_aaa'), + ), + << /aaa/111 first_aaa +-> /aaa/222 second_aaa +-> /aaa/333 third_aaa +/prefixed +-> /prefixed/group +-> -> /prefixed/group/aa aa +-> -> /prefixed/group/bb bb +-> -> /prefixed/group/cc cc +-> /prefixed root +-> /prefixed/group +-> -> /prefixed/group/dd dd +-> -> /prefixed/group/ee ee +-> -> /prefixed/group/ff ff +EOF + ), + + 'Group regardless of segments' => array( + array( + array('/aaa-111/', 'a1'), + array('/aaa-222/', 'a2'), + array('/aaa-333/', 'a3'), + array('/group-aa/', 'g1'), + array('/group-bb/', 'g2'), + array('/group-cc/', 'g3'), + ), + << /aaa-111 a1 +-> /aaa-222 a2 +-> /aaa-333 a3 +/group- +-> /group-aa g1 +-> /group-bb g2 +-> /group-cc g3 +EOF + ), + ); + } + + private function dumpCollection(StaticPrefixCollection $collection, $prefix = '') + { + $lines = array(); + + foreach ($collection->getItems() as $item) { + if ($item instanceof StaticPrefixCollection) { + $lines[] = $prefix.$item->getPrefix(); + $lines[] = $this->dumpCollection($item, $prefix.'-> '); + } else { + $lines[] = $prefix.implode(' ', $item); + } + } + + return implode("\n", $lines); + } +} diff --git a/lib/silex/vendor/symfony/routing/Tests/Matcher/RedirectableUrlMatcherTest.php b/lib/silex/vendor/symfony/routing/Tests/Matcher/RedirectableUrlMatcherTest.php new file mode 100644 index 000000000..0948e002a --- /dev/null +++ b/lib/silex/vendor/symfony/routing/Tests/Matcher/RedirectableUrlMatcherTest.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\Routing\Tests\Matcher; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Routing\Route; +use Symfony\Component\Routing\RouteCollection; +use Symfony\Component\Routing\RequestContext; + +class RedirectableUrlMatcherTest extends TestCase +{ + public function testRedirectWhenNoSlash() + { + $coll = new RouteCollection(); + $coll->add('foo', new Route('/foo/')); + + $matcher = $this->getMockForAbstractClass('Symfony\Component\Routing\Matcher\RedirectableUrlMatcher', array($coll, new RequestContext())); + $matcher->expects($this->once())->method('redirect')->will($this->returnValue(array())); + $matcher->match('/foo'); + } + + /** + * @expectedException \Symfony\Component\Routing\Exception\ResourceNotFoundException + */ + public function testRedirectWhenNoSlashForNonSafeMethod() + { + $coll = new RouteCollection(); + $coll->add('foo', new Route('/foo/')); + + $context = new RequestContext(); + $context->setMethod('POST'); + $matcher = $this->getMockForAbstractClass('Symfony\Component\Routing\Matcher\RedirectableUrlMatcher', array($coll, $context)); + $matcher->match('/foo'); + } + + public function testSchemeRedirectRedirectsToFirstScheme() + { + $coll = new RouteCollection(); + $coll->add('foo', new Route('/foo', array(), array(), array(), '', array('FTP', 'HTTPS'))); + + $matcher = $this->getMockForAbstractClass('Symfony\Component\Routing\Matcher\RedirectableUrlMatcher', array($coll, new RequestContext())); + $matcher + ->expects($this->once()) + ->method('redirect') + ->with('/foo', 'foo', 'ftp') + ->will($this->returnValue(array('_route' => 'foo'))) + ; + $matcher->match('/foo'); + } + + public function testNoSchemaRedirectIfOnOfMultipleSchemesMatches() + { + $coll = new RouteCollection(); + $coll->add('foo', new Route('/foo', array(), array(), array(), '', array('https', 'http'))); + + $matcher = $this->getMockForAbstractClass('Symfony\Component\Routing\Matcher\RedirectableUrlMatcher', array($coll, new RequestContext())); + $matcher + ->expects($this->never()) + ->method('redirect'); + $matcher->match('/foo'); + } + + public function testSchemeRedirectWithParams() + { + $coll = new RouteCollection(); + $coll->add('foo', new Route('/foo/{bar}', array(), array(), array(), '', array('https'))); + + $matcher = $this->getMockForAbstractClass('Symfony\Component\Routing\Matcher\RedirectableUrlMatcher', array($coll, new RequestContext())); + $matcher + ->expects($this->once()) + ->method('redirect') + ->with('/foo/baz', 'foo', 'https') + ->will($this->returnValue(array('redirect' => 'value'))) + ; + $this->assertEquals(array('_route' => 'foo', 'bar' => 'baz', 'redirect' => 'value'), $matcher->match('/foo/baz')); + } + + public function testSlashRedirectWithParams() + { + $coll = new RouteCollection(); + $coll->add('foo', new Route('/foo/{bar}/')); + + $matcher = $this->getMockForAbstractClass('Symfony\Component\Routing\Matcher\RedirectableUrlMatcher', array($coll, new RequestContext())); + $matcher + ->expects($this->once()) + ->method('redirect') + ->with('/foo/baz/', 'foo', null) + ->will($this->returnValue(array('redirect' => 'value'))) + ; + $this->assertEquals(array('_route' => 'foo', 'bar' => 'baz', 'redirect' => 'value'), $matcher->match('/foo/baz')); + } + + public function testRedirectPreservesUrlEncoding() + { + $coll = new RouteCollection(); + $coll->add('foo', new Route('/foo:bar/')); + + $matcher = $this->getMockForAbstractClass('Symfony\Component\Routing\Matcher\RedirectableUrlMatcher', array($coll, new RequestContext())); + $matcher->expects($this->once())->method('redirect')->with('/foo%3Abar/')->willReturn(array()); + $matcher->match('/foo%3Abar'); + } +} diff --git a/lib/silex/vendor/symfony/routing/Tests/Matcher/TraceableUrlMatcherTest.php b/lib/silex/vendor/symfony/routing/Tests/Matcher/TraceableUrlMatcherTest.php new file mode 100644 index 000000000..9f0529e2d --- /dev/null +++ b/lib/silex/vendor/symfony/routing/Tests/Matcher/TraceableUrlMatcherTest.php @@ -0,0 +1,122 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Routing\Tests\Matcher; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\Routing\Route; +use Symfony\Component\Routing\RouteCollection; +use Symfony\Component\Routing\RequestContext; +use Symfony\Component\Routing\Matcher\TraceableUrlMatcher; + +class TraceableUrlMatcherTest extends TestCase +{ + public function test() + { + $coll = new RouteCollection(); + $coll->add('foo', new Route('/foo', array(), array(), array(), '', array(), array('POST'))); + $coll->add('bar', new Route('/bar/{id}', array(), array('id' => '\d+'))); + $coll->add('bar1', new Route('/bar/{name}', array(), array('id' => '\w+'), array(), '', array(), array('POST'))); + $coll->add('bar2', new Route('/foo', array(), array(), array(), 'baz')); + $coll->add('bar3', new Route('/foo1', array(), array(), array(), 'baz')); + $coll->add('bar4', new Route('/foo2', array(), array(), array(), 'baz', array(), array(), 'context.getMethod() == "GET"')); + + $context = new RequestContext(); + $context->setHost('baz'); + + $matcher = new TraceableUrlMatcher($coll, $context); + $traces = $matcher->getTraces('/babar'); + $this->assertSame(array(0, 0, 0, 0, 0, 0), $this->getLevels($traces)); + + $traces = $matcher->getTraces('/foo'); + $this->assertSame(array(1, 0, 0, 2), $this->getLevels($traces)); + + $traces = $matcher->getTraces('/bar/12'); + $this->assertSame(array(0, 2), $this->getLevels($traces)); + + $traces = $matcher->getTraces('/bar/dd'); + $this->assertSame(array(0, 1, 1, 0, 0, 0), $this->getLevels($traces)); + + $traces = $matcher->getTraces('/foo1'); + $this->assertSame(array(0, 0, 0, 0, 2), $this->getLevels($traces)); + + $context->setMethod('POST'); + $traces = $matcher->getTraces('/foo'); + $this->assertSame(array(2), $this->getLevels($traces)); + + $traces = $matcher->getTraces('/bar/dd'); + $this->assertSame(array(0, 1, 2), $this->getLevels($traces)); + + $traces = $matcher->getTraces('/foo2'); + $this->assertSame(array(0, 0, 0, 0, 0, 1), $this->getLevels($traces)); + } + + public function testMatchRouteOnMultipleHosts() + { + $routes = new RouteCollection(); + $routes->add('first', new Route( + '/mypath/', + array('_controller' => 'MainBundle:Info:first'), + array(), + array(), + 'some.example.com' + )); + + $routes->add('second', new Route( + '/mypath/', + array('_controller' => 'MainBundle:Info:second'), + array(), + array(), + 'another.example.com' + )); + + $context = new RequestContext(); + $context->setHost('baz'); + + $matcher = new TraceableUrlMatcher($routes, $context); + + $traces = $matcher->getTraces('/mypath/'); + $this->assertSame( + array(TraceableUrlMatcher::ROUTE_ALMOST_MATCHES, TraceableUrlMatcher::ROUTE_ALMOST_MATCHES), + $this->getLevels($traces) + ); + } + + public function getLevels($traces) + { + $levels = array(); + foreach ($traces as $trace) { + $levels[] = $trace['level']; + } + + return $levels; + } + + public function testRoutesWithConditions() + { + $routes = new RouteCollection(); + $routes->add('foo', new Route('/foo', array(), array(), array(), 'baz', array(), array(), "request.headers.get('User-Agent') matches '/firefox/i'")); + + $context = new RequestContext(); + $context->setHost('baz'); + + $matcher = new TraceableUrlMatcher($routes, $context); + + $notMatchingRequest = Request::create('/foo', 'GET'); + $traces = $matcher->getTracesForRequest($notMatchingRequest); + $this->assertEquals("Condition \"request.headers.get('User-Agent') matches '/firefox/i'\" does not evaluate to \"true\"", $traces[0]['log']); + + $matchingRequest = Request::create('/foo', 'GET', array(), array(), array(), array('HTTP_USER_AGENT' => 'Firefox')); + $traces = $matcher->getTracesForRequest($matchingRequest); + $this->assertEquals('Route matches!', $traces[0]['log']); + } +} diff --git a/lib/silex/vendor/symfony/routing/Tests/Matcher/UrlMatcherTest.php b/lib/silex/vendor/symfony/routing/Tests/Matcher/UrlMatcherTest.php new file mode 100644 index 000000000..8545c2c29 --- /dev/null +++ b/lib/silex/vendor/symfony/routing/Tests/Matcher/UrlMatcherTest.php @@ -0,0 +1,441 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Routing\Tests\Matcher; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Routing\Exception\MethodNotAllowedException; +use Symfony\Component\Routing\Exception\ResourceNotFoundException; +use Symfony\Component\Routing\Matcher\UrlMatcher; +use Symfony\Component\Routing\Route; +use Symfony\Component\Routing\RouteCollection; +use Symfony\Component\Routing\RequestContext; + +class UrlMatcherTest extends TestCase +{ + public function testNoMethodSoAllowed() + { + $coll = new RouteCollection(); + $coll->add('foo', new Route('/foo')); + + $matcher = new UrlMatcher($coll, new RequestContext()); + $this->assertInternalType('array', $matcher->match('/foo')); + } + + public function testMethodNotAllowed() + { + $coll = new RouteCollection(); + $coll->add('foo', new Route('/foo', array(), array(), array(), '', array(), array('post'))); + + $matcher = new UrlMatcher($coll, new RequestContext()); + + try { + $matcher->match('/foo'); + $this->fail(); + } catch (MethodNotAllowedException $e) { + $this->assertEquals(array('POST'), $e->getAllowedMethods()); + } + } + + public function testHeadAllowedWhenRequirementContainsGet() + { + $coll = new RouteCollection(); + $coll->add('foo', new Route('/foo', array(), array(), array(), '', array(), array('get'))); + + $matcher = new UrlMatcher($coll, new RequestContext('', 'head')); + $this->assertInternalType('array', $matcher->match('/foo')); + } + + public function testMethodNotAllowedAggregatesAllowedMethods() + { + $coll = new RouteCollection(); + $coll->add('foo1', new Route('/foo', array(), array(), array(), '', array(), array('post'))); + $coll->add('foo2', new Route('/foo', array(), array(), array(), '', array(), array('put', 'delete'))); + + $matcher = new UrlMatcher($coll, new RequestContext()); + + try { + $matcher->match('/foo'); + $this->fail(); + } catch (MethodNotAllowedException $e) { + $this->assertEquals(array('POST', 'PUT', 'DELETE'), $e->getAllowedMethods()); + } + } + + public function testMatch() + { + // test the patterns are matched and parameters are returned + $collection = new RouteCollection(); + $collection->add('foo', new Route('/foo/{bar}')); + $matcher = new UrlMatcher($collection, new RequestContext()); + try { + $matcher->match('/no-match'); + $this->fail(); + } catch (ResourceNotFoundException $e) { + } + $this->assertEquals(array('_route' => 'foo', 'bar' => 'baz'), $matcher->match('/foo/baz')); + + // test that defaults are merged + $collection = new RouteCollection(); + $collection->add('foo', new Route('/foo/{bar}', array('def' => 'test'))); + $matcher = new UrlMatcher($collection, new RequestContext()); + $this->assertEquals(array('_route' => 'foo', 'bar' => 'baz', 'def' => 'test'), $matcher->match('/foo/baz')); + + // test that route "method" is ignored if no method is given in the context + $collection = new RouteCollection(); + $collection->add('foo', new Route('/foo', array(), array(), array(), '', array(), array('get', 'head'))); + $matcher = new UrlMatcher($collection, new RequestContext()); + $this->assertInternalType('array', $matcher->match('/foo')); + + // route does not match with POST method context + $matcher = new UrlMatcher($collection, new RequestContext('', 'post')); + try { + $matcher->match('/foo'); + $this->fail(); + } catch (MethodNotAllowedException $e) { + } + + // route does match with GET or HEAD method context + $matcher = new UrlMatcher($collection, new RequestContext()); + $this->assertInternalType('array', $matcher->match('/foo')); + $matcher = new UrlMatcher($collection, new RequestContext('', 'head')); + $this->assertInternalType('array', $matcher->match('/foo')); + + // route with an optional variable as the first segment + $collection = new RouteCollection(); + $collection->add('bar', new Route('/{bar}/foo', array('bar' => 'bar'), array('bar' => 'foo|bar'))); + $matcher = new UrlMatcher($collection, new RequestContext()); + $this->assertEquals(array('_route' => 'bar', 'bar' => 'bar'), $matcher->match('/bar/foo')); + $this->assertEquals(array('_route' => 'bar', 'bar' => 'foo'), $matcher->match('/foo/foo')); + + $collection = new RouteCollection(); + $collection->add('bar', new Route('/{bar}', array('bar' => 'bar'), array('bar' => 'foo|bar'))); + $matcher = new UrlMatcher($collection, new RequestContext()); + $this->assertEquals(array('_route' => 'bar', 'bar' => 'foo'), $matcher->match('/foo')); + $this->assertEquals(array('_route' => 'bar', 'bar' => 'bar'), $matcher->match('/')); + + // route with only optional variables + $collection = new RouteCollection(); + $collection->add('bar', new Route('/{foo}/{bar}', array('foo' => 'foo', 'bar' => 'bar'), array())); + $matcher = new UrlMatcher($collection, new RequestContext()); + $this->assertEquals(array('_route' => 'bar', 'foo' => 'foo', 'bar' => 'bar'), $matcher->match('/')); + $this->assertEquals(array('_route' => 'bar', 'foo' => 'a', 'bar' => 'bar'), $matcher->match('/a')); + $this->assertEquals(array('_route' => 'bar', 'foo' => 'a', 'bar' => 'b'), $matcher->match('/a/b')); + } + + public function testMatchWithPrefixes() + { + $collection = new RouteCollection(); + $collection->add('foo', new Route('/{foo}')); + $collection->addPrefix('/b'); + $collection->addPrefix('/a'); + + $matcher = new UrlMatcher($collection, new RequestContext()); + $this->assertEquals(array('_route' => 'foo', 'foo' => 'foo'), $matcher->match('/a/b/foo')); + } + + public function testMatchWithDynamicPrefix() + { + $collection = new RouteCollection(); + $collection->add('foo', new Route('/{foo}')); + $collection->addPrefix('/b'); + $collection->addPrefix('/{_locale}'); + + $matcher = new UrlMatcher($collection, new RequestContext()); + $this->assertEquals(array('_locale' => 'fr', '_route' => 'foo', 'foo' => 'foo'), $matcher->match('/fr/b/foo')); + } + + public function testMatchSpecialRouteName() + { + $collection = new RouteCollection(); + $collection->add('$péß^a|', new Route('/bar')); + + $matcher = new UrlMatcher($collection, new RequestContext()); + $this->assertEquals(array('_route' => '$péß^a|'), $matcher->match('/bar')); + } + + public function testMatchNonAlpha() + { + $collection = new RouteCollection(); + $chars = '!"$%éà &\'()*+,./:;<=>@ABCDEFGHIJKLMNOPQRSTUVWXYZ\\[]^_`abcdefghijklmnopqrstuvwxyz{|}~-'; + $collection->add('foo', new Route('/{foo}/bar', array(), array('foo' => '['.preg_quote($chars).']+'), array('utf8' => true))); + + $matcher = new UrlMatcher($collection, new RequestContext()); + $this->assertEquals(array('_route' => 'foo', 'foo' => $chars), $matcher->match('/'.rawurlencode($chars).'/bar')); + $this->assertEquals(array('_route' => 'foo', 'foo' => $chars), $matcher->match('/'.strtr($chars, array('%' => '%25')).'/bar')); + } + + public function testMatchWithDotMetacharacterInRequirements() + { + $collection = new RouteCollection(); + $collection->add('foo', new Route('/{foo}/bar', array(), array('foo' => '.+'))); + + $matcher = new UrlMatcher($collection, new RequestContext()); + $this->assertEquals(array('_route' => 'foo', 'foo' => "\n"), $matcher->match('/'.urlencode("\n").'/bar'), 'linefeed character is matched'); + } + + public function testMatchOverriddenRoute() + { + $collection = new RouteCollection(); + $collection->add('foo', new Route('/foo')); + + $collection1 = new RouteCollection(); + $collection1->add('foo', new Route('/foo1')); + + $collection->addCollection($collection1); + + $matcher = new UrlMatcher($collection, new RequestContext()); + + $this->assertEquals(array('_route' => 'foo'), $matcher->match('/foo1')); + $this->{method_exists($this, $_ = 'expectException') ? $_ : 'setExpectedException'}('Symfony\Component\Routing\Exception\ResourceNotFoundException'); + $this->assertEquals(array(), $matcher->match('/foo')); + } + + public function testMatchRegression() + { + $coll = new RouteCollection(); + $coll->add('foo', new Route('/foo/{foo}')); + $coll->add('bar', new Route('/foo/bar/{foo}')); + + $matcher = new UrlMatcher($coll, new RequestContext()); + $this->assertEquals(array('foo' => 'bar', '_route' => 'bar'), $matcher->match('/foo/bar/bar')); + + $collection = new RouteCollection(); + $collection->add('foo', new Route('/{bar}')); + $matcher = new UrlMatcher($collection, new RequestContext()); + try { + $matcher->match('/'); + $this->fail(); + } catch (ResourceNotFoundException $e) { + } + } + + public function testDefaultRequirementForOptionalVariables() + { + $coll = new RouteCollection(); + $coll->add('test', new Route('/{page}.{_format}', array('page' => 'index', '_format' => 'html'))); + + $matcher = new UrlMatcher($coll, new RequestContext()); + $this->assertEquals(array('page' => 'my-page', '_format' => 'xml', '_route' => 'test'), $matcher->match('/my-page.xml')); + } + + public function testMatchingIsEager() + { + $coll = new RouteCollection(); + $coll->add('test', new Route('/{foo}-{bar}-', array(), array('foo' => '.+', 'bar' => '.+'))); + + $matcher = new UrlMatcher($coll, new RequestContext()); + $this->assertEquals(array('foo' => 'text1-text2-text3', 'bar' => 'text4', '_route' => 'test'), $matcher->match('/text1-text2-text3-text4-')); + } + + public function testAdjacentVariables() + { + $coll = new RouteCollection(); + $coll->add('test', new Route('/{w}{x}{y}{z}.{_format}', array('z' => 'default-z', '_format' => 'html'), array('y' => 'y|Y'))); + + $matcher = new UrlMatcher($coll, new RequestContext()); + // 'w' eagerly matches as much as possible and the other variables match the remaining chars. + // This also shows that the variables w-z must all exclude the separating char (the dot '.' in this case) by default requirement. + // Otherwise they would also consume '.xml' and _format would never match as it's an optional variable. + $this->assertEquals(array('w' => 'wwwww', 'x' => 'x', 'y' => 'Y', 'z' => 'Z', '_format' => 'xml', '_route' => 'test'), $matcher->match('/wwwwwxYZ.xml')); + // As 'y' has custom requirement and can only be of value 'y|Y', it will leave 'ZZZ' to variable z. + // So with carefully chosen requirements adjacent variables, can be useful. + $this->assertEquals(array('w' => 'wwwww', 'x' => 'x', 'y' => 'y', 'z' => 'ZZZ', '_format' => 'html', '_route' => 'test'), $matcher->match('/wwwwwxyZZZ')); + // z and _format are optional. + $this->assertEquals(array('w' => 'wwwww', 'x' => 'x', 'y' => 'y', 'z' => 'default-z', '_format' => 'html', '_route' => 'test'), $matcher->match('/wwwwwxy')); + + $this->{method_exists($this, $_ = 'expectException') ? $_ : 'setExpectedException'}('Symfony\Component\Routing\Exception\ResourceNotFoundException'); + $matcher->match('/wxy.html'); + } + + public function testOptionalVariableWithNoRealSeparator() + { + $coll = new RouteCollection(); + $coll->add('test', new Route('/get{what}', array('what' => 'All'))); + $matcher = new UrlMatcher($coll, new RequestContext()); + + $this->assertEquals(array('what' => 'All', '_route' => 'test'), $matcher->match('/get')); + $this->assertEquals(array('what' => 'Sites', '_route' => 'test'), $matcher->match('/getSites')); + + // Usually the character in front of an optional parameter can be left out, e.g. with pattern '/get/{what}' just '/get' would match. + // But here the 't' in 'get' is not a separating character, so it makes no sense to match without it. + $this->{method_exists($this, $_ = 'expectException') ? $_ : 'setExpectedException'}('Symfony\Component\Routing\Exception\ResourceNotFoundException'); + $matcher->match('/ge'); + } + + public function testRequiredVariableWithNoRealSeparator() + { + $coll = new RouteCollection(); + $coll->add('test', new Route('/get{what}Suffix')); + $matcher = new UrlMatcher($coll, new RequestContext()); + + $this->assertEquals(array('what' => 'Sites', '_route' => 'test'), $matcher->match('/getSitesSuffix')); + } + + public function testDefaultRequirementOfVariable() + { + $coll = new RouteCollection(); + $coll->add('test', new Route('/{page}.{_format}')); + $matcher = new UrlMatcher($coll, new RequestContext()); + + $this->assertEquals(array('page' => 'index', '_format' => 'mobile.html', '_route' => 'test'), $matcher->match('/index.mobile.html')); + } + + /** + * @expectedException \Symfony\Component\Routing\Exception\ResourceNotFoundException + */ + public function testDefaultRequirementOfVariableDisallowsSlash() + { + $coll = new RouteCollection(); + $coll->add('test', new Route('/{page}.{_format}')); + $matcher = new UrlMatcher($coll, new RequestContext()); + + $matcher->match('/index.sl/ash'); + } + + /** + * @expectedException \Symfony\Component\Routing\Exception\ResourceNotFoundException + */ + public function testDefaultRequirementOfVariableDisallowsNextSeparator() + { + $coll = new RouteCollection(); + $coll->add('test', new Route('/{page}.{_format}', array(), array('_format' => 'html|xml'))); + $matcher = new UrlMatcher($coll, new RequestContext()); + + $matcher->match('/do.t.html'); + } + + /** + * @expectedException \Symfony\Component\Routing\Exception\ResourceNotFoundException + */ + public function testSchemeRequirement() + { + $coll = new RouteCollection(); + $coll->add('foo', new Route('/foo', array(), array(), array(), '', array('https'))); + $matcher = new UrlMatcher($coll, new RequestContext()); + $matcher->match('/foo'); + } + + /** + * @expectedException \Symfony\Component\Routing\Exception\ResourceNotFoundException + */ + public function testCondition() + { + $coll = new RouteCollection(); + $route = new Route('/foo'); + $route->setCondition('context.getMethod() == "POST"'); + $coll->add('foo', $route); + $matcher = new UrlMatcher($coll, new RequestContext()); + $matcher->match('/foo'); + } + + public function testRequestCondition() + { + $coll = new RouteCollection(); + $route = new Route('/foo/{bar}'); + $route->setCondition('request.getBaseUrl() == "/sub/front.php" and request.getPathInfo() == "/foo/bar"'); + $coll->add('foo', $route); + $matcher = new UrlMatcher($coll, new RequestContext('/sub/front.php')); + $this->assertEquals(array('bar' => 'bar', '_route' => 'foo'), $matcher->match('/foo/bar')); + } + + public function testDecodeOnce() + { + $coll = new RouteCollection(); + $coll->add('foo', new Route('/foo/{foo}')); + + $matcher = new UrlMatcher($coll, new RequestContext()); + $this->assertEquals(array('foo' => 'bar%23', '_route' => 'foo'), $matcher->match('/foo/bar%2523')); + } + + public function testCannotRelyOnPrefix() + { + $coll = new RouteCollection(); + + $subColl = new RouteCollection(); + $subColl->add('bar', new Route('/bar')); + $subColl->addPrefix('/prefix'); + // overwrite the pattern, so the prefix is not valid anymore for this route in the collection + $subColl->get('bar')->setPath('/new'); + + $coll->addCollection($subColl); + + $matcher = new UrlMatcher($coll, new RequestContext()); + $this->assertEquals(array('_route' => 'bar'), $matcher->match('/new')); + } + + public function testWithHost() + { + $coll = new RouteCollection(); + $coll->add('foo', new Route('/foo/{foo}', array(), array(), array(), '{locale}.example.com')); + + $matcher = new UrlMatcher($coll, new RequestContext('', 'GET', 'en.example.com')); + $this->assertEquals(array('foo' => 'bar', '_route' => 'foo', 'locale' => 'en'), $matcher->match('/foo/bar')); + } + + public function testWithHostOnRouteCollection() + { + $coll = new RouteCollection(); + $coll->add('foo', new Route('/foo/{foo}')); + $coll->add('bar', new Route('/bar/{foo}', array(), array(), array(), '{locale}.example.net')); + $coll->setHost('{locale}.example.com'); + + $matcher = new UrlMatcher($coll, new RequestContext('', 'GET', 'en.example.com')); + $this->assertEquals(array('foo' => 'bar', '_route' => 'foo', 'locale' => 'en'), $matcher->match('/foo/bar')); + + $matcher = new UrlMatcher($coll, new RequestContext('', 'GET', 'en.example.com')); + $this->assertEquals(array('foo' => 'bar', '_route' => 'bar', 'locale' => 'en'), $matcher->match('/bar/bar')); + } + + /** + * @expectedException \Symfony\Component\Routing\Exception\ResourceNotFoundException + */ + public function testWithOutHostHostDoesNotMatch() + { + $coll = new RouteCollection(); + $coll->add('foo', new Route('/foo/{foo}', array(), array(), array(), '{locale}.example.com')); + + $matcher = new UrlMatcher($coll, new RequestContext('', 'GET', 'example.com')); + $matcher->match('/foo/bar'); + } + + /** + * @expectedException \Symfony\Component\Routing\Exception\ResourceNotFoundException + */ + public function testPathIsCaseSensitive() + { + $coll = new RouteCollection(); + $coll->add('foo', new Route('/locale', array(), array('locale' => 'EN|FR|DE'))); + + $matcher = new UrlMatcher($coll, new RequestContext()); + $matcher->match('/en'); + } + + public function testHostIsCaseInsensitive() + { + $coll = new RouteCollection(); + $coll->add('foo', new Route('/', array(), array('locale' => 'EN|FR|DE'), array(), '{locale}.example.com')); + + $matcher = new UrlMatcher($coll, new RequestContext('', 'GET', 'en.example.com')); + $this->assertEquals(array('_route' => 'foo', 'locale' => 'en'), $matcher->match('/')); + } + + /** + * @expectedException \Symfony\Component\Routing\Exception\NoConfigurationException + */ + public function testNoConfiguration() + { + $coll = new RouteCollection(); + + $matcher = new UrlMatcher($coll, new RequestContext()); + $matcher->match('/'); + } +} diff --git a/lib/silex/vendor/symfony/routing/Tests/RequestContextTest.php b/lib/silex/vendor/symfony/routing/Tests/RequestContextTest.php new file mode 100644 index 000000000..ffe29d1a7 --- /dev/null +++ b/lib/silex/vendor/symfony/routing/Tests/RequestContextTest.php @@ -0,0 +1,160 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Routing\Tests; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\Routing\RequestContext; + +class RequestContextTest extends TestCase +{ + public function testConstruct() + { + $requestContext = new RequestContext( + 'foo', + 'post', + 'foo.bar', + 'HTTPS', + 8080, + 444, + '/baz', + 'bar=foobar' + ); + + $this->assertEquals('foo', $requestContext->getBaseUrl()); + $this->assertEquals('POST', $requestContext->getMethod()); + $this->assertEquals('foo.bar', $requestContext->getHost()); + $this->assertEquals('https', $requestContext->getScheme()); + $this->assertSame(8080, $requestContext->getHttpPort()); + $this->assertSame(444, $requestContext->getHttpsPort()); + $this->assertEquals('/baz', $requestContext->getPathInfo()); + $this->assertEquals('bar=foobar', $requestContext->getQueryString()); + } + + public function testFromRequest() + { + $request = Request::create('https://test.com:444/foo?bar=baz'); + $requestContext = new RequestContext(); + $requestContext->setHttpPort(123); + $requestContext->fromRequest($request); + + $this->assertEquals('', $requestContext->getBaseUrl()); + $this->assertEquals('GET', $requestContext->getMethod()); + $this->assertEquals('test.com', $requestContext->getHost()); + $this->assertEquals('https', $requestContext->getScheme()); + $this->assertEquals('/foo', $requestContext->getPathInfo()); + $this->assertEquals('bar=baz', $requestContext->getQueryString()); + $this->assertSame(123, $requestContext->getHttpPort()); + $this->assertSame(444, $requestContext->getHttpsPort()); + + $request = Request::create('http://test.com:8080/foo?bar=baz'); + $requestContext = new RequestContext(); + $requestContext->setHttpsPort(567); + $requestContext->fromRequest($request); + + $this->assertSame(8080, $requestContext->getHttpPort()); + $this->assertSame(567, $requestContext->getHttpsPort()); + } + + public function testGetParameters() + { + $requestContext = new RequestContext(); + $this->assertEquals(array(), $requestContext->getParameters()); + + $requestContext->setParameters(array('foo' => 'bar')); + $this->assertEquals(array('foo' => 'bar'), $requestContext->getParameters()); + } + + public function testHasParameter() + { + $requestContext = new RequestContext(); + $requestContext->setParameters(array('foo' => 'bar')); + + $this->assertTrue($requestContext->hasParameter('foo')); + $this->assertFalse($requestContext->hasParameter('baz')); + } + + public function testGetParameter() + { + $requestContext = new RequestContext(); + $requestContext->setParameters(array('foo' => 'bar')); + + $this->assertEquals('bar', $requestContext->getParameter('foo')); + $this->assertNull($requestContext->getParameter('baz')); + } + + public function testSetParameter() + { + $requestContext = new RequestContext(); + $requestContext->setParameter('foo', 'bar'); + + $this->assertEquals('bar', $requestContext->getParameter('foo')); + } + + public function testMethod() + { + $requestContext = new RequestContext(); + $requestContext->setMethod('post'); + + $this->assertSame('POST', $requestContext->getMethod()); + } + + public function testScheme() + { + $requestContext = new RequestContext(); + $requestContext->setScheme('HTTPS'); + + $this->assertSame('https', $requestContext->getScheme()); + } + + public function testHost() + { + $requestContext = new RequestContext(); + $requestContext->setHost('eXampLe.com'); + + $this->assertSame('example.com', $requestContext->getHost()); + } + + public function testQueryString() + { + $requestContext = new RequestContext(); + $requestContext->setQueryString(null); + + $this->assertSame('', $requestContext->getQueryString()); + } + + public function testPort() + { + $requestContext = new RequestContext(); + $requestContext->setHttpPort('123'); + $requestContext->setHttpsPort('456'); + + $this->assertSame(123, $requestContext->getHttpPort()); + $this->assertSame(456, $requestContext->getHttpsPort()); + } + + public function testFluentInterface() + { + $requestContext = new RequestContext(); + + $this->assertSame($requestContext, $requestContext->setBaseUrl('/app.php')); + $this->assertSame($requestContext, $requestContext->setPathInfo('/index')); + $this->assertSame($requestContext, $requestContext->setMethod('POST')); + $this->assertSame($requestContext, $requestContext->setScheme('https')); + $this->assertSame($requestContext, $requestContext->setHost('example.com')); + $this->assertSame($requestContext, $requestContext->setQueryString('foo=bar')); + $this->assertSame($requestContext, $requestContext->setHttpPort(80)); + $this->assertSame($requestContext, $requestContext->setHttpsPort(443)); + $this->assertSame($requestContext, $requestContext->setParameters(array())); + $this->assertSame($requestContext, $requestContext->setParameter('foo', 'bar')); + } +} diff --git a/lib/silex/vendor/symfony/routing/Tests/RouteCollectionBuilderTest.php b/lib/silex/vendor/symfony/routing/Tests/RouteCollectionBuilderTest.php new file mode 100644 index 000000000..f6af600bd --- /dev/null +++ b/lib/silex/vendor/symfony/routing/Tests/RouteCollectionBuilderTest.php @@ -0,0 +1,364 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Routing\Tests; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Config\FileLocator; +use Symfony\Component\Config\Resource\FileResource; +use Symfony\Component\Routing\Loader\YamlFileLoader; +use Symfony\Component\Routing\Route; +use Symfony\Component\Routing\RouteCollection; +use Symfony\Component\Routing\RouteCollectionBuilder; + +class RouteCollectionBuilderTest extends TestCase +{ + public function testImport() + { + $resolvedLoader = $this->getMockBuilder('Symfony\Component\Config\Loader\LoaderInterface')->getMock(); + $resolver = $this->getMockBuilder('Symfony\Component\Config\Loader\LoaderResolverInterface')->getMock(); + $resolver->expects($this->once()) + ->method('resolve') + ->with('admin_routing.yml', 'yaml') + ->will($this->returnValue($resolvedLoader)); + + $originalRoute = new Route('/foo/path'); + $expectedCollection = new RouteCollection(); + $expectedCollection->add('one_test_route', $originalRoute); + $expectedCollection->addResource(new FileResource(__DIR__.'/Fixtures/file_resource.yml')); + + $resolvedLoader + ->expects($this->once()) + ->method('load') + ->with('admin_routing.yml', 'yaml') + ->will($this->returnValue($expectedCollection)); + + $loader = $this->getMockBuilder('Symfony\Component\Config\Loader\LoaderInterface')->getMock(); + $loader->expects($this->any()) + ->method('getResolver') + ->will($this->returnValue($resolver)); + + // import the file! + $routes = new RouteCollectionBuilder($loader); + $importedRoutes = $routes->import('admin_routing.yml', '/', 'yaml'); + + // we should get back a RouteCollectionBuilder + $this->assertInstanceOf('Symfony\Component\Routing\RouteCollectionBuilder', $importedRoutes); + + // get the collection back so we can look at it + $addedCollection = $importedRoutes->build(); + $route = $addedCollection->get('one_test_route'); + $this->assertSame($originalRoute, $route); + // should return file_resource.yml, which is in the original collection + $this->assertCount(1, $addedCollection->getResources()); + + // make sure the routes were imported into the top-level builder + $routeCollection = $routes->build(); + $this->assertCount(1, $routes->build()); + $this->assertCount(1, $routeCollection->getResources()); + } + + public function testImportAddResources() + { + $routeCollectionBuilder = new RouteCollectionBuilder(new YamlFileLoader(new FileLocator(array(__DIR__.'/Fixtures/')))); + $routeCollectionBuilder->import('file_resource.yml'); + $routeCollection = $routeCollectionBuilder->build(); + + $this->assertCount(1, $routeCollection->getResources()); + } + + /** + * @expectedException \BadMethodCallException + */ + public function testImportWithoutLoaderThrowsException() + { + $collectionBuilder = new RouteCollectionBuilder(); + $collectionBuilder->import('routing.yml'); + } + + public function testAdd() + { + $collectionBuilder = new RouteCollectionBuilder(); + + $addedRoute = $collectionBuilder->add('/checkout', 'AppBundle:Order:checkout'); + $addedRoute2 = $collectionBuilder->add('/blogs', 'AppBundle:Blog:list', 'blog_list'); + $this->assertInstanceOf('Symfony\Component\Routing\Route', $addedRoute); + $this->assertEquals('AppBundle:Order:checkout', $addedRoute->getDefault('_controller')); + + $finalCollection = $collectionBuilder->build(); + $this->assertSame($addedRoute2, $finalCollection->get('blog_list')); + } + + public function testFlushOrdering() + { + $importedCollection = new RouteCollection(); + $importedCollection->add('imported_route1', new Route('/imported/foo1')); + $importedCollection->add('imported_route2', new Route('/imported/foo2')); + + $loader = $this->getMockBuilder('Symfony\Component\Config\Loader\LoaderInterface')->getMock(); + // make this loader able to do the import - keeps mocking simple + $loader->expects($this->any()) + ->method('supports') + ->will($this->returnValue(true)); + $loader + ->expects($this->once()) + ->method('load') + ->will($this->returnValue($importedCollection)); + + $routes = new RouteCollectionBuilder($loader); + + // 1) Add a route + $routes->add('/checkout', 'AppBundle:Order:checkout', 'checkout_route'); + // 2) Import from a file + $routes->mount('/', $routes->import('admin_routing.yml')); + // 3) Add another route + $routes->add('/', 'AppBundle:Default:homepage', 'homepage'); + // 4) Add another route + $routes->add('/admin', 'AppBundle:Admin:dashboard', 'admin_dashboard'); + + // set a default value + $routes->setDefault('_locale', 'fr'); + + $actualCollection = $routes->build(); + + $this->assertCount(5, $actualCollection); + $actualRouteNames = array_keys($actualCollection->all()); + $this->assertEquals(array( + 'checkout_route', + 'imported_route1', + 'imported_route2', + 'homepage', + 'admin_dashboard', + ), $actualRouteNames); + + // make sure the defaults were set + $checkoutRoute = $actualCollection->get('checkout_route'); + $defaults = $checkoutRoute->getDefaults(); + $this->assertArrayHasKey('_locale', $defaults); + $this->assertEquals('fr', $defaults['_locale']); + } + + public function testFlushSetsRouteNames() + { + $collectionBuilder = new RouteCollectionBuilder(); + + // add a "named" route + $collectionBuilder->add('/admin', 'AppBundle:Admin:dashboard', 'admin_dashboard'); + // add an unnamed route + $collectionBuilder->add('/blogs', 'AppBundle:Blog:list') + ->setMethods(array('GET')); + + // integer route names are allowed - they don't confuse things + $collectionBuilder->add('/products', 'AppBundle:Product:list', 100); + + $actualCollection = $collectionBuilder->build(); + $actualRouteNames = array_keys($actualCollection->all()); + $this->assertEquals(array( + 'admin_dashboard', + 'GET_blogs', + '100', + ), $actualRouteNames); + } + + public function testFlushSetsDetailsOnChildrenRoutes() + { + $routes = new RouteCollectionBuilder(); + + $routes->add('/blogs/{page}', 'listAction', 'blog_list') + // unique things for the route + ->setDefault('page', 1) + ->setRequirement('id', '\d+') + ->setOption('expose', true) + // things that the collection will try to override (but won't) + ->setDefault('_format', 'html') + ->setRequirement('_format', 'json|xml') + ->setOption('fooBar', true) + ->setHost('example.com') + ->setCondition('request.isSecure()') + ->setSchemes(array('https')) + ->setMethods(array('POST')); + + // a simple route, nothing added to it + $routes->add('/blogs/{id}', 'editAction', 'blog_edit'); + + // configure the collection itself + $routes + // things that will not override the child route + ->setDefault('_format', 'json') + ->setRequirement('_format', 'xml') + ->setOption('fooBar', false) + ->setHost('symfony.com') + ->setCondition('request.query.get("page")==1') + // some unique things that should be set on the child + ->setDefault('_locale', 'fr') + ->setRequirement('_locale', 'fr|en') + ->setOption('niceRoute', true) + ->setSchemes(array('http')) + ->setMethods(array('GET', 'POST')); + + $collection = $routes->build(); + $actualListRoute = $collection->get('blog_list'); + + $this->assertEquals(1, $actualListRoute->getDefault('page')); + $this->assertEquals('\d+', $actualListRoute->getRequirement('id')); + $this->assertTrue($actualListRoute->getOption('expose')); + // none of these should be overridden + $this->assertEquals('html', $actualListRoute->getDefault('_format')); + $this->assertEquals('json|xml', $actualListRoute->getRequirement('_format')); + $this->assertTrue($actualListRoute->getOption('fooBar')); + $this->assertEquals('example.com', $actualListRoute->getHost()); + $this->assertEquals('request.isSecure()', $actualListRoute->getCondition()); + $this->assertEquals(array('https'), $actualListRoute->getSchemes()); + $this->assertEquals(array('POST'), $actualListRoute->getMethods()); + // inherited from the main collection + $this->assertEquals('fr', $actualListRoute->getDefault('_locale')); + $this->assertEquals('fr|en', $actualListRoute->getRequirement('_locale')); + $this->assertTrue($actualListRoute->getOption('niceRoute')); + + $actualEditRoute = $collection->get('blog_edit'); + // inherited from the collection + $this->assertEquals('symfony.com', $actualEditRoute->getHost()); + $this->assertEquals('request.query.get("page")==1', $actualEditRoute->getCondition()); + $this->assertEquals(array('http'), $actualEditRoute->getSchemes()); + $this->assertEquals(array('GET', 'POST'), $actualEditRoute->getMethods()); + } + + /** + * @dataProvider providePrefixTests + */ + public function testFlushPrefixesPaths($collectionPrefix, $routePath, $expectedPath) + { + $routes = new RouteCollectionBuilder(); + + $routes->add($routePath, 'someController', 'test_route'); + + $outerRoutes = new RouteCollectionBuilder(); + $outerRoutes->mount($collectionPrefix, $routes); + + $collection = $outerRoutes->build(); + + $this->assertEquals($expectedPath, $collection->get('test_route')->getPath()); + } + + public function providePrefixTests() + { + $tests = array(); + // empty prefix is of course ok + $tests[] = array('', '/foo', '/foo'); + // normal prefix - does not matter if it's a wildcard + $tests[] = array('/{admin}', '/foo', '/{admin}/foo'); + // shows that a prefix will always be given the starting slash + $tests[] = array('0', '/foo', '/0/foo'); + + // spaces are ok, and double slahses at the end are cleaned + $tests[] = array('/ /', '/foo', '/ /foo'); + + return $tests; + } + + public function testFlushSetsPrefixedWithMultipleLevels() + { + $loader = $this->getMockBuilder('Symfony\Component\Config\Loader\LoaderInterface')->getMock(); + $routes = new RouteCollectionBuilder($loader); + + $routes->add('homepage', 'MainController::homepageAction', 'homepage'); + + $adminRoutes = $routes->createBuilder(); + $adminRoutes->add('/dashboard', 'AdminController::dashboardAction', 'admin_dashboard'); + + // embedded collection under /admin + $adminBlogRoutes = $routes->createBuilder(); + $adminBlogRoutes->add('/new', 'BlogController::newAction', 'admin_blog_new'); + // mount into admin, but before the parent collection has been mounted + $adminRoutes->mount('/blog', $adminBlogRoutes); + + // now mount the /admin routes, above should all still be /blog/admin + $routes->mount('/admin', $adminRoutes); + // add a route after mounting + $adminRoutes->add('/users', 'AdminController::userAction', 'admin_users'); + + // add another sub-collection after the mount + $otherAdminRoutes = $routes->createBuilder(); + $otherAdminRoutes->add('/sales', 'StatsController::indexAction', 'admin_stats_sales'); + $adminRoutes->mount('/stats', $otherAdminRoutes); + + // add a normal collection and see that it is also prefixed + $importedCollection = new RouteCollection(); + $importedCollection->add('imported_route', new Route('/foo')); + // make this loader able to do the import - keeps mocking simple + $loader->expects($this->any()) + ->method('supports') + ->will($this->returnValue(true)); + $loader + ->expects($this->any()) + ->method('load') + ->will($this->returnValue($importedCollection)); + // import this from the /admin route builder + $adminRoutes->import('admin.yml', '/imported'); + + $collection = $routes->build(); + $this->assertEquals('/admin/dashboard', $collection->get('admin_dashboard')->getPath(), 'Routes before mounting have the prefix'); + $this->assertEquals('/admin/users', $collection->get('admin_users')->getPath(), 'Routes after mounting have the prefix'); + $this->assertEquals('/admin/blog/new', $collection->get('admin_blog_new')->getPath(), 'Sub-collections receive prefix even if mounted before parent prefix'); + $this->assertEquals('/admin/stats/sales', $collection->get('admin_stats_sales')->getPath(), 'Sub-collections receive prefix if mounted after parent prefix'); + $this->assertEquals('/admin/imported/foo', $collection->get('imported_route')->getPath(), 'Normal RouteCollections are also prefixed properly'); + } + + public function testAutomaticRouteNamesDoNotConflict() + { + $routes = new RouteCollectionBuilder(); + + $adminRoutes = $routes->createBuilder(); + // route 1 + $adminRoutes->add('/dashboard', ''); + + $accountRoutes = $routes->createBuilder(); + // route 2 + $accountRoutes->add('/dashboard', '') + ->setMethods(array('GET')); + // route 3 + $accountRoutes->add('/dashboard', '') + ->setMethods(array('POST')); + + $routes->mount('/admin', $adminRoutes); + $routes->mount('/account', $accountRoutes); + + $collection = $routes->build(); + // there are 2 routes (i.e. with non-conflicting names) + $this->assertCount(3, $collection->all()); + } + + public function testAddsThePrefixOnlyOnceWhenLoadingMultipleCollections() + { + $firstCollection = new RouteCollection(); + $firstCollection->add('a', new Route('/a')); + + $secondCollection = new RouteCollection(); + $secondCollection->add('b', new Route('/b')); + + $loader = $this->getMockBuilder('Symfony\Component\Config\Loader\LoaderInterface')->getMock(); + $loader->expects($this->any()) + ->method('supports') + ->will($this->returnValue(true)); + $loader + ->expects($this->any()) + ->method('load') + ->will($this->returnValue(array($firstCollection, $secondCollection))); + + $routeCollectionBuilder = new RouteCollectionBuilder($loader); + $routeCollectionBuilder->import('/directory/recurse/*', '/other/', 'glob'); + $routes = $routeCollectionBuilder->build()->all(); + + $this->assertEquals(2, count($routes)); + $this->assertEquals('/other/a', $routes['a']->getPath()); + $this->assertEquals('/other/b', $routes['b']->getPath()); + } +} diff --git a/lib/silex/vendor/symfony/routing/Tests/RouteCollectionTest.php b/lib/silex/vendor/symfony/routing/Tests/RouteCollectionTest.php new file mode 100644 index 000000000..83457ff14 --- /dev/null +++ b/lib/silex/vendor/symfony/routing/Tests/RouteCollectionTest.php @@ -0,0 +1,305 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Routing\Tests; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Routing\RouteCollection; +use Symfony\Component\Routing\Route; +use Symfony\Component\Config\Resource\FileResource; + +class RouteCollectionTest extends TestCase +{ + public function testRoute() + { + $collection = new RouteCollection(); + $route = new Route('/foo'); + $collection->add('foo', $route); + $this->assertEquals(array('foo' => $route), $collection->all(), '->add() adds a route'); + $this->assertEquals($route, $collection->get('foo'), '->get() returns a route by name'); + $this->assertNull($collection->get('bar'), '->get() returns null if a route does not exist'); + } + + public function testOverriddenRoute() + { + $collection = new RouteCollection(); + $collection->add('foo', new Route('/foo')); + $collection->add('foo', new Route('/foo1')); + + $this->assertEquals('/foo1', $collection->get('foo')->getPath()); + } + + public function testDeepOverriddenRoute() + { + $collection = new RouteCollection(); + $collection->add('foo', new Route('/foo')); + + $collection1 = new RouteCollection(); + $collection1->add('foo', new Route('/foo1')); + + $collection2 = new RouteCollection(); + $collection2->add('foo', new Route('/foo2')); + + $collection1->addCollection($collection2); + $collection->addCollection($collection1); + + $this->assertEquals('/foo2', $collection1->get('foo')->getPath()); + $this->assertEquals('/foo2', $collection->get('foo')->getPath()); + } + + public function testIterator() + { + $collection = new RouteCollection(); + $collection->add('foo', new Route('/foo')); + + $collection1 = new RouteCollection(); + $collection1->add('bar', $bar = new Route('/bar')); + $collection1->add('foo', $foo = new Route('/foo-new')); + $collection->addCollection($collection1); + $collection->add('last', $last = new Route('/last')); + + $this->assertInstanceOf('\ArrayIterator', $collection->getIterator()); + $this->assertSame(array('bar' => $bar, 'foo' => $foo, 'last' => $last), $collection->getIterator()->getArrayCopy()); + } + + public function testCount() + { + $collection = new RouteCollection(); + $collection->add('foo', new Route('/foo')); + + $collection1 = new RouteCollection(); + $collection1->add('bar', new Route('/bar')); + $collection->addCollection($collection1); + + $this->assertCount(2, $collection); + } + + public function testAddCollection() + { + $collection = new RouteCollection(); + $collection->add('foo', new Route('/foo')); + + $collection1 = new RouteCollection(); + $collection1->add('bar', $bar = new Route('/bar')); + $collection1->add('foo', $foo = new Route('/foo-new')); + + $collection2 = new RouteCollection(); + $collection2->add('grandchild', $grandchild = new Route('/grandchild')); + + $collection1->addCollection($collection2); + $collection->addCollection($collection1); + $collection->add('last', $last = new Route('/last')); + + $this->assertSame(array('bar' => $bar, 'foo' => $foo, 'grandchild' => $grandchild, 'last' => $last), $collection->all(), + '->addCollection() imports routes of another collection, overrides if necessary and adds them at the end'); + } + + public function testAddCollectionWithResources() + { + $collection = new RouteCollection(); + $collection->addResource($foo = new FileResource(__DIR__.'/Fixtures/foo.xml')); + $collection1 = new RouteCollection(); + $collection1->addResource($foo1 = new FileResource(__DIR__.'/Fixtures/foo1.xml')); + $collection->addCollection($collection1); + $this->assertEquals(array($foo, $foo1), $collection->getResources(), '->addCollection() merges resources'); + } + + public function testAddDefaultsAndRequirementsAndOptions() + { + $collection = new RouteCollection(); + $collection->add('foo', new Route('/{placeholder}')); + $collection1 = new RouteCollection(); + $collection1->add('bar', new Route('/{placeholder}', + array('_controller' => 'fixed', 'placeholder' => 'default'), array('placeholder' => '.+'), array('option' => 'value')) + ); + $collection->addCollection($collection1); + + $collection->addDefaults(array('placeholder' => 'new-default')); + $this->assertEquals(array('placeholder' => 'new-default'), $collection->get('foo')->getDefaults(), '->addDefaults() adds defaults to all routes'); + $this->assertEquals(array('_controller' => 'fixed', 'placeholder' => 'new-default'), $collection->get('bar')->getDefaults(), + '->addDefaults() adds defaults to all routes and overwrites existing ones'); + + $collection->addRequirements(array('placeholder' => '\d+')); + $this->assertEquals(array('placeholder' => '\d+'), $collection->get('foo')->getRequirements(), '->addRequirements() adds requirements to all routes'); + $this->assertEquals(array('placeholder' => '\d+'), $collection->get('bar')->getRequirements(), + '->addRequirements() adds requirements to all routes and overwrites existing ones'); + + $collection->addOptions(array('option' => 'new-value')); + $this->assertEquals( + array('option' => 'new-value', 'compiler_class' => 'Symfony\\Component\\Routing\\RouteCompiler'), + $collection->get('bar')->getOptions(), '->addOptions() adds options to all routes and overwrites existing ones' + ); + } + + public function testAddPrefix() + { + $collection = new RouteCollection(); + $collection->add('foo', $foo = new Route('/foo')); + $collection2 = new RouteCollection(); + $collection2->add('bar', $bar = new Route('/bar')); + $collection->addCollection($collection2); + $collection->addPrefix(' / '); + $this->assertSame('/foo', $collection->get('foo')->getPath(), '->addPrefix() trims the prefix and a single slash has no effect'); + $collection->addPrefix('/{admin}', array('admin' => 'admin'), array('admin' => '\d+')); + $this->assertEquals('/{admin}/foo', $collection->get('foo')->getPath(), '->addPrefix() adds a prefix to all routes'); + $this->assertEquals('/{admin}/bar', $collection->get('bar')->getPath(), '->addPrefix() adds a prefix to all routes'); + $this->assertEquals(array('admin' => 'admin'), $collection->get('foo')->getDefaults(), '->addPrefix() adds defaults to all routes'); + $this->assertEquals(array('admin' => 'admin'), $collection->get('bar')->getDefaults(), '->addPrefix() adds defaults to all routes'); + $this->assertEquals(array('admin' => '\d+'), $collection->get('foo')->getRequirements(), '->addPrefix() adds requirements to all routes'); + $this->assertEquals(array('admin' => '\d+'), $collection->get('bar')->getRequirements(), '->addPrefix() adds requirements to all routes'); + $collection->addPrefix('0'); + $this->assertEquals('/0/{admin}/foo', $collection->get('foo')->getPath(), '->addPrefix() ensures a prefix must start with a slash and must not end with a slash'); + $collection->addPrefix('/ /'); + $this->assertSame('/ /0/{admin}/foo', $collection->get('foo')->getPath(), '->addPrefix() can handle spaces if desired'); + $this->assertSame('/ /0/{admin}/bar', $collection->get('bar')->getPath(), 'the route pattern of an added collection is in synch with the added prefix'); + } + + public function testAddPrefixOverridesDefaultsAndRequirements() + { + $collection = new RouteCollection(); + $collection->add('foo', $foo = new Route('/foo.{_format}')); + $collection->add('bar', $bar = new Route('/bar.{_format}', array(), array('_format' => 'json'))); + $collection->addPrefix('/admin', array(), array('_format' => 'html')); + + $this->assertEquals('html', $collection->get('foo')->getRequirement('_format'), '->addPrefix() overrides existing requirements'); + $this->assertEquals('html', $collection->get('bar')->getRequirement('_format'), '->addPrefix() overrides existing requirements'); + } + + public function testResource() + { + $collection = new RouteCollection(); + $collection->addResource($foo = new FileResource(__DIR__.'/Fixtures/foo.xml')); + $collection->addResource($bar = new FileResource(__DIR__.'/Fixtures/bar.xml')); + $collection->addResource(new FileResource(__DIR__.'/Fixtures/foo.xml')); + + $this->assertEquals(array($foo, $bar), $collection->getResources(), + '->addResource() adds a resource and getResources() only returns unique ones by comparing the string representation'); + } + + public function testUniqueRouteWithGivenName() + { + $collection1 = new RouteCollection(); + $collection1->add('foo', new Route('/old')); + $collection2 = new RouteCollection(); + $collection3 = new RouteCollection(); + $collection3->add('foo', $new = new Route('/new')); + + $collection2->addCollection($collection3); + $collection1->addCollection($collection2); + + $this->assertSame($new, $collection1->get('foo'), '->get() returns new route that overrode previous one'); + // size of 1 because collection1 contains /new but not /old anymore + $this->assertCount(1, $collection1->getIterator(), '->addCollection() removes previous routes when adding new routes with the same name'); + } + + public function testGet() + { + $collection1 = new RouteCollection(); + $collection1->add('a', $a = new Route('/a')); + $collection2 = new RouteCollection(); + $collection2->add('b', $b = new Route('/b')); + $collection1->addCollection($collection2); + $collection1->add('$péß^a|', $c = new Route('/special')); + + $this->assertSame($b, $collection1->get('b'), '->get() returns correct route in child collection'); + $this->assertSame($c, $collection1->get('$péß^a|'), '->get() can handle special characters'); + $this->assertNull($collection2->get('a'), '->get() does not return the route defined in parent collection'); + $this->assertNull($collection1->get('non-existent'), '->get() returns null when route does not exist'); + $this->assertNull($collection1->get(0), '->get() does not disclose internal child RouteCollection'); + } + + public function testRemove() + { + $collection = new RouteCollection(); + $collection->add('foo', $foo = new Route('/foo')); + + $collection1 = new RouteCollection(); + $collection1->add('bar', $bar = new Route('/bar')); + $collection->addCollection($collection1); + $collection->add('last', $last = new Route('/last')); + + $collection->remove('foo'); + $this->assertSame(array('bar' => $bar, 'last' => $last), $collection->all(), '->remove() can remove a single route'); + $collection->remove(array('bar', 'last')); + $this->assertSame(array(), $collection->all(), '->remove() accepts an array and can remove multiple routes at once'); + } + + public function testSetHost() + { + $collection = new RouteCollection(); + $routea = new Route('/a'); + $routeb = new Route('/b', array(), array(), array(), '{locale}.example.net'); + $collection->add('a', $routea); + $collection->add('b', $routeb); + + $collection->setHost('{locale}.example.com'); + + $this->assertEquals('{locale}.example.com', $routea->getHost()); + $this->assertEquals('{locale}.example.com', $routeb->getHost()); + } + + public function testSetCondition() + { + $collection = new RouteCollection(); + $routea = new Route('/a'); + $routeb = new Route('/b', array(), array(), array(), '{locale}.example.net', array(), array(), 'context.getMethod() == "GET"'); + $collection->add('a', $routea); + $collection->add('b', $routeb); + + $collection->setCondition('context.getMethod() == "POST"'); + + $this->assertEquals('context.getMethod() == "POST"', $routea->getCondition()); + $this->assertEquals('context.getMethod() == "POST"', $routeb->getCondition()); + } + + public function testClone() + { + $collection = new RouteCollection(); + $collection->add('a', new Route('/a')); + $collection->add('b', new Route('/b', array('placeholder' => 'default'), array('placeholder' => '.+'))); + + $clonedCollection = clone $collection; + + $this->assertCount(2, $clonedCollection); + $this->assertEquals($collection->get('a'), $clonedCollection->get('a')); + $this->assertNotSame($collection->get('a'), $clonedCollection->get('a')); + $this->assertEquals($collection->get('b'), $clonedCollection->get('b')); + $this->assertNotSame($collection->get('b'), $clonedCollection->get('b')); + } + + public function testSetSchemes() + { + $collection = new RouteCollection(); + $routea = new Route('/a', array(), array(), array(), '', 'http'); + $routeb = new Route('/b'); + $collection->add('a', $routea); + $collection->add('b', $routeb); + + $collection->setSchemes(array('http', 'https')); + + $this->assertEquals(array('http', 'https'), $routea->getSchemes()); + $this->assertEquals(array('http', 'https'), $routeb->getSchemes()); + } + + public function testSetMethods() + { + $collection = new RouteCollection(); + $routea = new Route('/a', array(), array(), array(), '', array(), array('GET', 'POST')); + $routeb = new Route('/b'); + $collection->add('a', $routea); + $collection->add('b', $routeb); + + $collection->setMethods('PUT'); + + $this->assertEquals(array('PUT'), $routea->getMethods()); + $this->assertEquals(array('PUT'), $routeb->getMethods()); + } +} diff --git a/lib/silex/vendor/symfony/routing/Tests/RouteCompilerTest.php b/lib/silex/vendor/symfony/routing/Tests/RouteCompilerTest.php new file mode 100644 index 000000000..54006d7ed --- /dev/null +++ b/lib/silex/vendor/symfony/routing/Tests/RouteCompilerTest.php @@ -0,0 +1,389 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Routing\Tests; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Routing\Route; +use Symfony\Component\Routing\RouteCompiler; + +class RouteCompilerTest extends TestCase +{ + /** + * @dataProvider provideCompileData + */ + public function testCompile($name, $arguments, $prefix, $regex, $variables, $tokens) + { + $r = new \ReflectionClass('Symfony\\Component\\Routing\\Route'); + $route = $r->newInstanceArgs($arguments); + + $compiled = $route->compile(); + $this->assertEquals($prefix, $compiled->getStaticPrefix(), $name.' (static prefix)'); + $this->assertEquals($regex, $compiled->getRegex(), $name.' (regex)'); + $this->assertEquals($variables, $compiled->getVariables(), $name.' (variables)'); + $this->assertEquals($tokens, $compiled->getTokens(), $name.' (tokens)'); + } + + public function provideCompileData() + { + return array( + array( + 'Static route', + array('/foo'), + '/foo', '#^/foo$#s', array(), array( + array('text', '/foo'), + ), + ), + + array( + 'Route with a variable', + array('/foo/{bar}'), + '/foo', '#^/foo/(?P[^/]++)$#s', array('bar'), array( + array('variable', '/', '[^/]++', 'bar'), + array('text', '/foo'), + ), + ), + + array( + 'Route with a variable that has a default value', + array('/foo/{bar}', array('bar' => 'bar')), + '/foo', '#^/foo(?:/(?P[^/]++))?$#s', array('bar'), array( + array('variable', '/', '[^/]++', 'bar'), + array('text', '/foo'), + ), + ), + + array( + 'Route with several variables', + array('/foo/{bar}/{foobar}'), + '/foo', '#^/foo/(?P[^/]++)/(?P[^/]++)$#s', array('bar', 'foobar'), array( + array('variable', '/', '[^/]++', 'foobar'), + array('variable', '/', '[^/]++', 'bar'), + array('text', '/foo'), + ), + ), + + array( + 'Route with several variables that have default values', + array('/foo/{bar}/{foobar}', array('bar' => 'bar', 'foobar' => '')), + '/foo', '#^/foo(?:/(?P[^/]++)(?:/(?P[^/]++))?)?$#s', array('bar', 'foobar'), array( + array('variable', '/', '[^/]++', 'foobar'), + array('variable', '/', '[^/]++', 'bar'), + array('text', '/foo'), + ), + ), + + array( + 'Route with several variables but some of them have no default values', + array('/foo/{bar}/{foobar}', array('bar' => 'bar')), + '/foo', '#^/foo/(?P[^/]++)/(?P[^/]++)$#s', array('bar', 'foobar'), array( + array('variable', '/', '[^/]++', 'foobar'), + array('variable', '/', '[^/]++', 'bar'), + array('text', '/foo'), + ), + ), + + array( + 'Route with an optional variable as the first segment', + array('/{bar}', array('bar' => 'bar')), + '', '#^/(?P[^/]++)?$#s', array('bar'), array( + array('variable', '/', '[^/]++', 'bar'), + ), + ), + + array( + 'Route with a requirement of 0', + array('/{bar}', array('bar' => null), array('bar' => '0')), + '', '#^/(?P0)?$#s', array('bar'), array( + array('variable', '/', '0', 'bar'), + ), + ), + + array( + 'Route with an optional variable as the first segment with requirements', + array('/{bar}', array('bar' => 'bar'), array('bar' => '(foo|bar)')), + '', '#^/(?P(foo|bar))?$#s', array('bar'), array( + array('variable', '/', '(foo|bar)', 'bar'), + ), + ), + + array( + 'Route with only optional variables', + array('/{foo}/{bar}', array('foo' => 'foo', 'bar' => 'bar')), + '', '#^/(?P[^/]++)?(?:/(?P[^/]++))?$#s', array('foo', 'bar'), array( + array('variable', '/', '[^/]++', 'bar'), + array('variable', '/', '[^/]++', 'foo'), + ), + ), + + array( + 'Route with a variable in last position', + array('/foo-{bar}'), + '/foo-', '#^/foo\-(?P[^/]++)$#s', array('bar'), array( + array('variable', '-', '[^/]++', 'bar'), + array('text', '/foo'), + ), + ), + + array( + 'Route with nested placeholders', + array('/{static{var}static}'), + '/{static', '#^/\{static(?P[^/]+)static\}$#s', array('var'), array( + array('text', 'static}'), + array('variable', '', '[^/]+', 'var'), + array('text', '/{static'), + ), + ), + + array( + 'Route without separator between variables', + array('/{w}{x}{y}{z}.{_format}', array('z' => 'default-z', '_format' => 'html'), array('y' => '(y|Y)')), + '', '#^/(?P[^/\.]+)(?P[^/\.]+)(?P(y|Y))(?:(?P[^/\.]++)(?:\.(?P<_format>[^/]++))?)?$#s', array('w', 'x', 'y', 'z', '_format'), array( + array('variable', '.', '[^/]++', '_format'), + array('variable', '', '[^/\.]++', 'z'), + array('variable', '', '(y|Y)', 'y'), + array('variable', '', '[^/\.]+', 'x'), + array('variable', '/', '[^/\.]+', 'w'), + ), + ), + + array( + 'Route with a format', + array('/foo/{bar}.{_format}'), + '/foo', '#^/foo/(?P[^/\.]++)\.(?P<_format>[^/]++)$#s', array('bar', '_format'), array( + array('variable', '.', '[^/]++', '_format'), + array('variable', '/', '[^/\.]++', 'bar'), + array('text', '/foo'), + ), + ), + + array( + 'Static non UTF-8 route', + array("/fo\xE9"), + "/fo\xE9", "#^/fo\xE9$#s", array(), array( + array('text', "/fo\xE9"), + ), + ), + + array( + 'Route with an explicit UTF-8 requirement', + array('/{bar}', array('bar' => null), array('bar' => '.'), array('utf8' => true)), + '', '#^/(?P.)?$#su', array('bar'), array( + array('variable', '/', '.', 'bar', true), + ), + ), + ); + } + + /** + * @group legacy + * @dataProvider provideCompileImplicitUtf8Data + * @expectedDeprecation Using UTF-8 route %s without setting the "utf8" option is deprecated %s. + */ + public function testCompileImplicitUtf8Data($name, $arguments, $prefix, $regex, $variables, $tokens, $deprecationType) + { + $r = new \ReflectionClass('Symfony\\Component\\Routing\\Route'); + $route = $r->newInstanceArgs($arguments); + + $compiled = $route->compile(); + $this->assertEquals($prefix, $compiled->getStaticPrefix(), $name.' (static prefix)'); + $this->assertEquals($regex, $compiled->getRegex(), $name.' (regex)'); + $this->assertEquals($variables, $compiled->getVariables(), $name.' (variables)'); + $this->assertEquals($tokens, $compiled->getTokens(), $name.' (tokens)'); + } + + public function provideCompileImplicitUtf8Data() + { + return array( + array( + 'Static UTF-8 route', + array('/foé'), + '/foé', '#^/foé$#su', array(), array( + array('text', '/foé'), + ), + 'patterns', + ), + + array( + 'Route with an implicit UTF-8 requirement', + array('/{bar}', array('bar' => null), array('bar' => 'é')), + '', '#^/(?Pé)?$#su', array('bar'), array( + array('variable', '/', 'é', 'bar', true), + ), + 'requirements', + ), + + array( + 'Route with a UTF-8 class requirement', + array('/{bar}', array('bar' => null), array('bar' => '\pM')), + '', '#^/(?P\pM)?$#su', array('bar'), array( + array('variable', '/', '\pM', 'bar', true), + ), + 'requirements', + ), + + array( + 'Route with a UTF-8 separator', + array('/foo/{bar}§{_format}', array(), array(), array('compiler_class' => Utf8RouteCompiler::class)), + '/foo', '#^/foo/(?P[^/§]++)§(?P<_format>[^/]++)$#su', array('bar', '_format'), array( + array('variable', '§', '[^/]++', '_format', true), + array('variable', '/', '[^/§]++', 'bar', true), + array('text', '/foo'), + ), + 'patterns', + ), + ); + } + + /** + * @expectedException \LogicException + */ + public function testRouteWithSameVariableTwice() + { + $route = new Route('/{name}/{name}'); + + $compiled = $route->compile(); + } + + /** + * @expectedException \LogicException + */ + public function testRouteCharsetMismatch() + { + $route = new Route("/\xE9/{bar}", array(), array('bar' => '.'), array('utf8' => true)); + + $compiled = $route->compile(); + } + + /** + * @expectedException \LogicException + */ + public function testRequirementCharsetMismatch() + { + $route = new Route('/foo/{bar}', array(), array('bar' => "\xE9"), array('utf8' => true)); + + $compiled = $route->compile(); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testRouteWithFragmentAsPathParameter() + { + $route = new Route('/{_fragment}'); + + $compiled = $route->compile(); + } + + /** + * @dataProvider getVariableNamesStartingWithADigit + * @expectedException \DomainException + */ + public function testRouteWithVariableNameStartingWithADigit($name) + { + $route = new Route('/{'.$name.'}'); + $route->compile(); + } + + public function getVariableNamesStartingWithADigit() + { + return array( + array('09'), + array('123'), + array('1e2'), + ); + } + + /** + * @dataProvider provideCompileWithHostData + */ + public function testCompileWithHost($name, $arguments, $prefix, $regex, $variables, $pathVariables, $tokens, $hostRegex, $hostVariables, $hostTokens) + { + $r = new \ReflectionClass('Symfony\\Component\\Routing\\Route'); + $route = $r->newInstanceArgs($arguments); + + $compiled = $route->compile(); + $this->assertEquals($prefix, $compiled->getStaticPrefix(), $name.' (static prefix)'); + $this->assertEquals($regex, str_replace(array("\n", ' '), '', $compiled->getRegex()), $name.' (regex)'); + $this->assertEquals($variables, $compiled->getVariables(), $name.' (variables)'); + $this->assertEquals($pathVariables, $compiled->getPathVariables(), $name.' (path variables)'); + $this->assertEquals($tokens, $compiled->getTokens(), $name.' (tokens)'); + $this->assertEquals($hostRegex, str_replace(array("\n", ' '), '', $compiled->getHostRegex()), $name.' (host regex)'); + $this->assertEquals($hostVariables, $compiled->getHostVariables(), $name.' (host variables)'); + $this->assertEquals($hostTokens, $compiled->getHostTokens(), $name.' (host tokens)'); + } + + public function provideCompileWithHostData() + { + return array( + array( + 'Route with host pattern', + array('/hello', array(), array(), array(), 'www.example.com'), + '/hello', '#^/hello$#s', array(), array(), array( + array('text', '/hello'), + ), + '#^www\.example\.com$#si', array(), array( + array('text', 'www.example.com'), + ), + ), + array( + 'Route with host pattern and some variables', + array('/hello/{name}', array(), array(), array(), 'www.example.{tld}'), + '/hello', '#^/hello/(?P[^/]++)$#s', array('tld', 'name'), array('name'), array( + array('variable', '/', '[^/]++', 'name'), + array('text', '/hello'), + ), + '#^www\.example\.(?P[^\.]++)$#si', array('tld'), array( + array('variable', '.', '[^\.]++', 'tld'), + array('text', 'www.example'), + ), + ), + array( + 'Route with variable at beginning of host', + array('/hello', array(), array(), array(), '{locale}.example.{tld}'), + '/hello', '#^/hello$#s', array('locale', 'tld'), array(), array( + array('text', '/hello'), + ), + '#^(?P[^\.]++)\.example\.(?P[^\.]++)$#si', array('locale', 'tld'), array( + array('variable', '.', '[^\.]++', 'tld'), + array('text', '.example'), + array('variable', '', '[^\.]++', 'locale'), + ), + ), + array( + 'Route with host variables that has a default value', + array('/hello', array('locale' => 'a', 'tld' => 'b'), array(), array(), '{locale}.example.{tld}'), + '/hello', '#^/hello$#s', array('locale', 'tld'), array(), array( + array('text', '/hello'), + ), + '#^(?P[^\.]++)\.example\.(?P[^\.]++)$#si', array('locale', 'tld'), array( + array('variable', '.', '[^\.]++', 'tld'), + array('text', '.example'), + array('variable', '', '[^\.]++', 'locale'), + ), + ), + ); + } + + /** + * @expectedException \DomainException + */ + public function testRouteWithTooLongVariableName() + { + $route = new Route(sprintf('/{%s}', str_repeat('a', RouteCompiler::VARIABLE_MAXIMUM_LENGTH + 1))); + $route->compile(); + } +} + +class Utf8RouteCompiler extends RouteCompiler +{ + const SEPARATORS = '/§'; +} diff --git a/lib/silex/vendor/symfony/routing/Tests/RouteTest.php b/lib/silex/vendor/symfony/routing/Tests/RouteTest.php new file mode 100644 index 000000000..ff7e320c5 --- /dev/null +++ b/lib/silex/vendor/symfony/routing/Tests/RouteTest.php @@ -0,0 +1,258 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Routing\Tests; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Routing\Route; + +class RouteTest extends TestCase +{ + public function testConstructor() + { + $route = new Route('/{foo}', array('foo' => 'bar'), array('foo' => '\d+'), array('foo' => 'bar'), '{locale}.example.com'); + $this->assertEquals('/{foo}', $route->getPath(), '__construct() takes a path as its first argument'); + $this->assertEquals(array('foo' => 'bar'), $route->getDefaults(), '__construct() takes defaults as its second argument'); + $this->assertEquals(array('foo' => '\d+'), $route->getRequirements(), '__construct() takes requirements as its third argument'); + $this->assertEquals('bar', $route->getOption('foo'), '__construct() takes options as its fourth argument'); + $this->assertEquals('{locale}.example.com', $route->getHost(), '__construct() takes a host pattern as its fifth argument'); + + $route = new Route('/', array(), array(), array(), '', array('Https'), array('POST', 'put'), 'context.getMethod() == "GET"'); + $this->assertEquals(array('https'), $route->getSchemes(), '__construct() takes schemes as its sixth argument and lowercases it'); + $this->assertEquals(array('POST', 'PUT'), $route->getMethods(), '__construct() takes methods as its seventh argument and uppercases it'); + $this->assertEquals('context.getMethod() == "GET"', $route->getCondition(), '__construct() takes a condition as its eight argument'); + + $route = new Route('/', array(), array(), array(), '', 'Https', 'Post'); + $this->assertEquals(array('https'), $route->getSchemes(), '__construct() takes a single scheme as its sixth argument'); + $this->assertEquals(array('POST'), $route->getMethods(), '__construct() takes a single method as its seventh argument'); + } + + public function testPath() + { + $route = new Route('/{foo}'); + $route->setPath('/{bar}'); + $this->assertEquals('/{bar}', $route->getPath(), '->setPath() sets the path'); + $route->setPath(''); + $this->assertEquals('/', $route->getPath(), '->setPath() adds a / at the beginning of the path if needed'); + $route->setPath('bar'); + $this->assertEquals('/bar', $route->getPath(), '->setPath() adds a / at the beginning of the path if needed'); + $this->assertEquals($route, $route->setPath(''), '->setPath() implements a fluent interface'); + $route->setPath('//path'); + $this->assertEquals('/path', $route->getPath(), '->setPath() does not allow two slashes "//" at the beginning of the path as it would be confused with a network path when generating the path from the route'); + } + + public function testOptions() + { + $route = new Route('/{foo}'); + $route->setOptions(array('foo' => 'bar')); + $this->assertEquals(array_merge(array( + 'compiler_class' => 'Symfony\\Component\\Routing\\RouteCompiler', + ), array('foo' => 'bar')), $route->getOptions(), '->setOptions() sets the options'); + $this->assertEquals($route, $route->setOptions(array()), '->setOptions() implements a fluent interface'); + + $route->setOptions(array('foo' => 'foo')); + $route->addOptions(array('bar' => 'bar')); + $this->assertEquals($route, $route->addOptions(array()), '->addOptions() implements a fluent interface'); + $this->assertEquals(array('foo' => 'foo', 'bar' => 'bar', 'compiler_class' => 'Symfony\\Component\\Routing\\RouteCompiler'), $route->getOptions(), '->addDefaults() keep previous defaults'); + } + + public function testOption() + { + $route = new Route('/{foo}'); + $this->assertFalse($route->hasOption('foo'), '->hasOption() return false if option is not set'); + $this->assertEquals($route, $route->setOption('foo', 'bar'), '->setOption() implements a fluent interface'); + $this->assertEquals('bar', $route->getOption('foo'), '->setOption() sets the option'); + $this->assertTrue($route->hasOption('foo'), '->hasOption() return true if option is set'); + } + + public function testDefaults() + { + $route = new Route('/{foo}'); + $route->setDefaults(array('foo' => 'bar')); + $this->assertEquals(array('foo' => 'bar'), $route->getDefaults(), '->setDefaults() sets the defaults'); + $this->assertEquals($route, $route->setDefaults(array()), '->setDefaults() implements a fluent interface'); + + $route->setDefault('foo', 'bar'); + $this->assertEquals('bar', $route->getDefault('foo'), '->setDefault() sets a default value'); + + $route->setDefault('foo2', 'bar2'); + $this->assertEquals('bar2', $route->getDefault('foo2'), '->getDefault() return the default value'); + $this->assertNull($route->getDefault('not_defined'), '->getDefault() return null if default value is not set'); + + $route->setDefault('_controller', $closure = function () { return 'Hello'; }); + $this->assertEquals($closure, $route->getDefault('_controller'), '->setDefault() sets a default value'); + + $route->setDefaults(array('foo' => 'foo')); + $route->addDefaults(array('bar' => 'bar')); + $this->assertEquals($route, $route->addDefaults(array()), '->addDefaults() implements a fluent interface'); + $this->assertEquals(array('foo' => 'foo', 'bar' => 'bar'), $route->getDefaults(), '->addDefaults() keep previous defaults'); + } + + public function testRequirements() + { + $route = new Route('/{foo}'); + $route->setRequirements(array('foo' => '\d+')); + $this->assertEquals(array('foo' => '\d+'), $route->getRequirements(), '->setRequirements() sets the requirements'); + $this->assertEquals('\d+', $route->getRequirement('foo'), '->getRequirement() returns a requirement'); + $this->assertNull($route->getRequirement('bar'), '->getRequirement() returns null if a requirement is not defined'); + $route->setRequirements(array('foo' => '^\d+$')); + $this->assertEquals('\d+', $route->getRequirement('foo'), '->getRequirement() removes ^ and $ from the path'); + $this->assertEquals($route, $route->setRequirements(array()), '->setRequirements() implements a fluent interface'); + + $route->setRequirements(array('foo' => '\d+')); + $route->addRequirements(array('bar' => '\d+')); + $this->assertEquals($route, $route->addRequirements(array()), '->addRequirements() implements a fluent interface'); + $this->assertEquals(array('foo' => '\d+', 'bar' => '\d+'), $route->getRequirements(), '->addRequirement() keep previous requirements'); + } + + public function testRequirement() + { + $route = new Route('/{foo}'); + $this->assertFalse($route->hasRequirement('foo'), '->hasRequirement() return false if requirement is not set'); + $route->setRequirement('foo', '^\d+$'); + $this->assertEquals('\d+', $route->getRequirement('foo'), '->setRequirement() removes ^ and $ from the path'); + $this->assertTrue($route->hasRequirement('foo'), '->hasRequirement() return true if requirement is set'); + } + + /** + * @dataProvider getInvalidRequirements + * @expectedException \InvalidArgumentException + */ + public function testSetInvalidRequirement($req) + { + $route = new Route('/{foo}'); + $route->setRequirement('foo', $req); + } + + public function getInvalidRequirements() + { + return array( + array(''), + array(array()), + array('^$'), + array('^'), + array('$'), + ); + } + + public function testHost() + { + $route = new Route('/'); + $route->setHost('{locale}.example.net'); + $this->assertEquals('{locale}.example.net', $route->getHost(), '->setHost() sets the host pattern'); + } + + public function testScheme() + { + $route = new Route('/'); + $this->assertEquals(array(), $route->getSchemes(), 'schemes is initialized with array()'); + $this->assertFalse($route->hasScheme('http')); + $route->setSchemes('hTTp'); + $this->assertEquals(array('http'), $route->getSchemes(), '->setSchemes() accepts a single scheme string and lowercases it'); + $this->assertTrue($route->hasScheme('htTp')); + $this->assertFalse($route->hasScheme('httpS')); + $route->setSchemes(array('HttpS', 'hTTp')); + $this->assertEquals(array('https', 'http'), $route->getSchemes(), '->setSchemes() accepts an array of schemes and lowercases them'); + $this->assertTrue($route->hasScheme('htTp')); + $this->assertTrue($route->hasScheme('httpS')); + } + + public function testMethod() + { + $route = new Route('/'); + $this->assertEquals(array(), $route->getMethods(), 'methods is initialized with array()'); + $route->setMethods('gEt'); + $this->assertEquals(array('GET'), $route->getMethods(), '->setMethods() accepts a single method string and uppercases it'); + $route->setMethods(array('gEt', 'PosT')); + $this->assertEquals(array('GET', 'POST'), $route->getMethods(), '->setMethods() accepts an array of methods and uppercases them'); + } + + public function testCondition() + { + $route = new Route('/'); + $this->assertSame('', $route->getCondition()); + $route->setCondition('context.getMethod() == "GET"'); + $this->assertSame('context.getMethod() == "GET"', $route->getCondition()); + } + + public function testCompile() + { + $route = new Route('/{foo}'); + $this->assertInstanceOf('Symfony\Component\Routing\CompiledRoute', $compiled = $route->compile(), '->compile() returns a compiled route'); + $this->assertSame($compiled, $route->compile(), '->compile() only compiled the route once if unchanged'); + $route->setRequirement('foo', '.*'); + $this->assertNotSame($compiled, $route->compile(), '->compile() recompiles if the route was modified'); + } + + public function testSerialize() + { + $route = new Route('/prefix/{foo}', array('foo' => 'default'), array('foo' => '\d+')); + + $serialized = serialize($route); + $unserialized = unserialize($serialized); + + $this->assertEquals($route, $unserialized); + $this->assertNotSame($route, $unserialized); + } + + /** + * Tests that the compiled version is also serialized to prevent the overhead + * of compiling it again after unserialize. + */ + public function testSerializeWhenCompiled() + { + $route = new Route('/prefix/{foo}', array('foo' => 'default'), array('foo' => '\d+')); + $route->setHost('{locale}.example.net'); + $route->compile(); + + $serialized = serialize($route); + $unserialized = unserialize($serialized); + + $this->assertEquals($route, $unserialized); + $this->assertNotSame($route, $unserialized); + } + + /** + * Tests that unserialization does not fail when the compiled Route is of a + * class other than CompiledRoute, such as a subclass of it. + */ + public function testSerializeWhenCompiledWithClass() + { + $route = new Route('/', array(), array(), array('compiler_class' => '\Symfony\Component\Routing\Tests\Fixtures\CustomRouteCompiler')); + $this->assertInstanceOf('\Symfony\Component\Routing\Tests\Fixtures\CustomCompiledRoute', $route->compile(), '->compile() returned a proper route'); + + $serialized = serialize($route); + try { + $unserialized = unserialize($serialized); + $this->assertInstanceOf('\Symfony\Component\Routing\Tests\Fixtures\CustomCompiledRoute', $unserialized->compile(), 'the unserialized route compiled successfully'); + } catch (\Exception $e) { + $this->fail('unserializing a route which uses a custom compiled route class'); + } + } + + /** + * Tests that the serialized representation of a route in one symfony version + * also works in later symfony versions, i.e. the unserialized route is in the + * same state as another, semantically equivalent, route. + */ + public function testSerializedRepresentationKeepsWorking() + { + $serialized = 'C:31:"Symfony\Component\Routing\Route":934:{a:8:{s:4:"path";s:13:"/prefix/{foo}";s:4:"host";s:20:"{locale}.example.net";s:8:"defaults";a:1:{s:3:"foo";s:7:"default";}s:12:"requirements";a:1:{s:3:"foo";s:3:"\d+";}s:7:"options";a:1:{s:14:"compiler_class";s:39:"Symfony\Component\Routing\RouteCompiler";}s:7:"schemes";a:0:{}s:7:"methods";a:0:{}s:8:"compiled";C:39:"Symfony\Component\Routing\CompiledRoute":569:{a:8:{s:4:"vars";a:2:{i:0;s:6:"locale";i:1;s:3:"foo";}s:11:"path_prefix";s:7:"/prefix";s:10:"path_regex";s:30:"#^/prefix(?:/(?P\d+))?$#s";s:11:"path_tokens";a:2:{i:0;a:4:{i:0;s:8:"variable";i:1;s:1:"/";i:2;s:3:"\d+";i:3;s:3:"foo";}i:1;a:2:{i:0;s:4:"text";i:1;s:7:"/prefix";}}s:9:"path_vars";a:1:{i:0;s:3:"foo";}s:10:"host_regex";s:39:"#^(?P[^\.]++)\.example\.net$#si";s:11:"host_tokens";a:2:{i:0;a:2:{i:0;s:4:"text";i:1;s:12:".example.net";}i:1;a:4:{i:0;s:8:"variable";i:1;s:0:"";i:2;s:7:"[^\.]++";i:3;s:6:"locale";}}s:9:"host_vars";a:1:{i:0;s:6:"locale";}}}}}'; + $unserialized = unserialize($serialized); + + $route = new Route('/prefix/{foo}', array('foo' => 'default'), array('foo' => '\d+')); + $route->setHost('{locale}.example.net'); + $route->compile(); + + $this->assertEquals($route, $unserialized); + $this->assertNotSame($route, $unserialized); + } +} diff --git a/lib/silex/vendor/symfony/routing/Tests/RouterTest.php b/lib/silex/vendor/symfony/routing/Tests/RouterTest.php new file mode 100644 index 000000000..409959eec --- /dev/null +++ b/lib/silex/vendor/symfony/routing/Tests/RouterTest.php @@ -0,0 +1,162 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Routing\Tests; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Routing\Router; +use Symfony\Component\HttpFoundation\Request; + +class RouterTest extends TestCase +{ + private $router = null; + + private $loader = null; + + protected function setUp() + { + $this->loader = $this->getMockBuilder('Symfony\Component\Config\Loader\LoaderInterface')->getMock(); + $this->router = new Router($this->loader, 'routing.yml'); + } + + public function testSetOptionsWithSupportedOptions() + { + $this->router->setOptions(array( + 'cache_dir' => './cache', + 'debug' => true, + 'resource_type' => 'ResourceType', + )); + + $this->assertSame('./cache', $this->router->getOption('cache_dir')); + $this->assertTrue($this->router->getOption('debug')); + $this->assertSame('ResourceType', $this->router->getOption('resource_type')); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage The Router does not support the following options: "option_foo", "option_bar" + */ + public function testSetOptionsWithUnsupportedOptions() + { + $this->router->setOptions(array( + 'cache_dir' => './cache', + 'option_foo' => true, + 'option_bar' => 'baz', + 'resource_type' => 'ResourceType', + )); + } + + public function testSetOptionWithSupportedOption() + { + $this->router->setOption('cache_dir', './cache'); + + $this->assertSame('./cache', $this->router->getOption('cache_dir')); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage The Router does not support the "option_foo" option + */ + public function testSetOptionWithUnsupportedOption() + { + $this->router->setOption('option_foo', true); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage The Router does not support the "option_foo" option + */ + public function testGetOptionWithUnsupportedOption() + { + $this->router->getOption('option_foo', true); + } + + public function testThatRouteCollectionIsLoaded() + { + $this->router->setOption('resource_type', 'ResourceType'); + + $routeCollection = $this->getMockBuilder('Symfony\Component\Routing\RouteCollection')->getMock(); + + $this->loader->expects($this->once()) + ->method('load')->with('routing.yml', 'ResourceType') + ->will($this->returnValue($routeCollection)); + + $this->assertSame($routeCollection, $this->router->getRouteCollection()); + } + + /** + * @dataProvider provideMatcherOptionsPreventingCaching + */ + public function testMatcherIsCreatedIfCacheIsNotConfigured($option) + { + $this->router->setOption($option, null); + + $this->loader->expects($this->once()) + ->method('load')->with('routing.yml', null) + ->will($this->returnValue($this->getMockBuilder('Symfony\Component\Routing\RouteCollection')->getMock())); + + $this->assertInstanceOf('Symfony\\Component\\Routing\\Matcher\\UrlMatcher', $this->router->getMatcher()); + } + + public function provideMatcherOptionsPreventingCaching() + { + return array( + array('cache_dir'), + array('matcher_cache_class'), + ); + } + + /** + * @dataProvider provideGeneratorOptionsPreventingCaching + */ + public function testGeneratorIsCreatedIfCacheIsNotConfigured($option) + { + $this->router->setOption($option, null); + + $this->loader->expects($this->once()) + ->method('load')->with('routing.yml', null) + ->will($this->returnValue($this->getMockBuilder('Symfony\Component\Routing\RouteCollection')->getMock())); + + $this->assertInstanceOf('Symfony\\Component\\Routing\\Generator\\UrlGenerator', $this->router->getGenerator()); + } + + public function provideGeneratorOptionsPreventingCaching() + { + return array( + array('cache_dir'), + array('generator_cache_class'), + ); + } + + public function testMatchRequestWithUrlMatcherInterface() + { + $matcher = $this->getMockBuilder('Symfony\Component\Routing\Matcher\UrlMatcherInterface')->getMock(); + $matcher->expects($this->once())->method('match'); + + $p = new \ReflectionProperty($this->router, 'matcher'); + $p->setAccessible(true); + $p->setValue($this->router, $matcher); + + $this->router->matchRequest(Request::create('/')); + } + + public function testMatchRequestWithRequestMatcherInterface() + { + $matcher = $this->getMockBuilder('Symfony\Component\Routing\Matcher\RequestMatcherInterface')->getMock(); + $matcher->expects($this->once())->method('matchRequest'); + + $p = new \ReflectionProperty($this->router, 'matcher'); + $p->setAccessible(true); + $p->setValue($this->router, $matcher); + + $this->router->matchRequest(Request::create('/')); + } +} diff --git a/lib/silex/vendor/symfony/routing/composer.json b/lib/silex/vendor/symfony/routing/composer.json new file mode 100644 index 000000000..2ad746ec5 --- /dev/null +++ b/lib/silex/vendor/symfony/routing/composer.json @@ -0,0 +1,56 @@ +{ + "name": "symfony/routing", + "type": "library", + "description": "Symfony Routing Component", + "keywords": ["routing", "router", "URL", "URI"], + "homepage": "https://symfony.com", + "license": "MIT", + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "require": { + "php": "^5.5.9|>=7.0.8" + }, + "require-dev": { + "symfony/config": "~2.8|~3.0|~4.0", + "symfony/http-foundation": "~2.8|~3.0|~4.0", + "symfony/yaml": "~3.4|~4.0", + "symfony/expression-language": "~2.8|~3.0|~4.0", + "symfony/dependency-injection": "~3.3|~4.0", + "doctrine/annotations": "~1.0", + "doctrine/common": "~2.2", + "psr/log": "~1.0" + }, + "conflict": { + "symfony/config": "<2.8", + "symfony/dependency-injection": "<3.3", + "symfony/yaml": "<3.4" + }, + "suggest": { + "symfony/http-foundation": "For using a Symfony Request object", + "symfony/config": "For using the all-in-one router or any loader", + "symfony/yaml": "For using the YAML loader", + "symfony/expression-language": "For using expression matching", + "doctrine/annotations": "For using the annotation loader", + "symfony/dependency-injection": "For loading routes from a service" + }, + "autoload": { + "psr-4": { "Symfony\\Component\\Routing\\": "" }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "minimum-stability": "dev", + "extra": { + "branch-alias": { + "dev-master": "3.4-dev" + } + } +} diff --git a/lib/silex/vendor/symfony/routing/phpunit.xml.dist b/lib/silex/vendor/symfony/routing/phpunit.xml.dist new file mode 100644 index 000000000..bcc095952 --- /dev/null +++ b/lib/silex/vendor/symfony/routing/phpunit.xml.dist @@ -0,0 +1,30 @@ + + + + + + + + + + ./Tests/ + + + + + + ./ + + ./Tests + ./vendor + + + + diff --git a/lib/silex/vendor/twig/twig/.editorconfig b/lib/silex/vendor/twig/twig/.editorconfig new file mode 100644 index 000000000..270f1d1b7 --- /dev/null +++ b/lib/silex/vendor/twig/twig/.editorconfig @@ -0,0 +1,18 @@ +; top-most EditorConfig file +root = true + +; Unix-style newlines +[*] +end_of_line = LF + +[*.php] +indent_style = space +indent_size = 4 + +[*.test] +indent_style = space +indent_size = 4 + +[*.rst] +indent_style = space +indent_size = 4 diff --git a/lib/silex/vendor/twig/twig/.php_cs.dist b/lib/silex/vendor/twig/twig/.php_cs.dist new file mode 100644 index 000000000..d249ffbe4 --- /dev/null +++ b/lib/silex/vendor/twig/twig/.php_cs.dist @@ -0,0 +1,16 @@ +setRules(array( + '@Symfony' => true, + '@Symfony:risky' => true, + 'array_syntax' => array('syntax' => 'long'), + 'php_unit_fqcn_annotation' => false, + 'no_unreachable_default_argument_value' => false, + 'braces' => array('allow_single_line_closure' => true), + 'heredoc_to_nowdoc' => false, + 'dir_constant' => false, + )) + ->setRiskyAllowed(true) + ->setFinder(PhpCsFixer\Finder::create()->in(__DIR__)) +; diff --git a/lib/silex/vendor/twig/twig/.travis.yml b/lib/silex/vendor/twig/twig/.travis.yml new file mode 100644 index 000000000..147eb7df3 --- /dev/null +++ b/lib/silex/vendor/twig/twig/.travis.yml @@ -0,0 +1,49 @@ +language: php + +sudo: false + +cache: + directories: + - vendor + - $HOME/.composer/cache/files + +php: + - 5.3 + - 5.4 + - 5.5 + - 5.6 + - 7.0 + - 7.1 + - nightly + +env: + - TWIG_EXT=no + - TWIG_EXT=yes + +before_install: + # turn off XDebug + - phpenv config-rm xdebug.ini || return 0 + +install: + - travis_retry composer install + +before_script: + - if [ "$TWIG_EXT" == "yes" ]; then sh -c "cd ext/twig && phpize && ./configure --enable-twig && make && make install"; fi + - if [ "$TWIG_EXT" == "yes" ]; then echo "extension=twig.so" >> `php --ini | grep "Loaded Configuration" | sed -e "s|.*:\s*||"`; fi + +script: | + if [[ $TRAVIS_PHP_VERSION = 7.* || $TRAVIS_PHP_VERSION = nightly ]]; then + SYMFONY_PHPUNIT_VERSION=6.1 ./vendor/bin/simple-phpunit + else + ./vendor/bin/simple-phpunit + fi + +matrix: + fast_finish: true + exclude: + - php: 7.0 + env: TWIG_EXT=yes + - php: 7.1 + env: TWIG_EXT=yes + - php: nightly + env: TWIG_EXT=yes diff --git a/lib/silex/vendor/twig/twig/CHANGELOG b/lib/silex/vendor/twig/twig/CHANGELOG index ee447b066..f738c841c 100644 --- a/lib/silex/vendor/twig/twig/CHANGELOG +++ b/lib/silex/vendor/twig/twig/CHANGELOG @@ -1,3 +1,216 @@ +* 1.35.0 (2017-09-27) + + * added Twig_Profiler_Profile::reset() + * fixed use TokenParser to return an empty Node + * added RuntimeExtensionInterface + * added circular reference detection when loading templates + +* 1.34.4 (2017-07-04) + + * added support for runtime loaders in IntegrationTestCase + * fixed deprecation when using Twig_Profiler_Dumper_Html + +* 1.34.3 (2017-06-07) + + * fixed namespaces introduction + +* 1.34.2 (2017-06-05) + + * fixed namespaces introduction + +* 1.34.1 (2017-06-05) + + * fixed namespaces introduction + +* 1.34.0 (2017-06-05) + + * added support for PHPUnit 6 when testing extensions + * fixed PHP 7.2 compatibility + * fixed template name generation in Twig_Environment::createTemplate() + * removed final tag on Twig_TokenParser_Include + * added namespaced aliases for all (non-deprecated) classes and interfaces + * dropped HHVM support + * dropped PHP 5.2 support + +* 1.33.2 (2017-04-20) + + * fixed edge case in the method cache for Twig attributes + +* 1.33.1 (2017-04-18) + + * fixed the empty() test + +* 1.33.0 (2017-03-22) + + * fixed a race condition handling when writing cache files + * "length" filter now returns string length when applied to an object that does + not implement \Countable but provides __toString() + * "empty" test will now consider the return value of the __toString() method for + objects implement __toString() but not \Countable + * fixed JS escaping for unicode characters with higher code points + +* 1.32.0 (2017-02-26) + + * fixed deprecation notice in Twig_Util_DeprecationCollector + * added a PSR-11 compatible runtime loader + * added `side` argument to `trim` to allow left or right trimming only. + +* 1.31.0 (2017-01-11) + + * added Twig_NodeCaptureInterface for nodes that capture all output + * fixed marking the environment as initialized too early + * fixed C89 compat for the C extension + * turned fatal error into exception when a previously generated cache is corrupted + * fixed offline cache warm-ups for embedded templates + +* 1.30.0 (2016-12-23) + + * added Twig_FactoryRuntimeLoader + * deprecated function/test/filter/tag overriding + * deprecated the "disable_c_ext" attribute on Twig_Node_Expression_GetAttr + +* 1.29.0 (2016-12-13) + + * fixed sandbox being left enabled if an exception is thrown while rendering + * marked some classes as being final (via @final) + * made Twig_Error report real source path when possible + * added support for {{ _self }} to provide an upgrade path from 1.x to 2.0 (replaces {{ _self.templateName }}) + * deprecated silent display of undefined blocks + * deprecated support for mbstring.func_overload != 0 + +* 1.28.2 (2016-11-23) + + * fixed precedence between getFoo() and isFoo() in Twig_Template::getAttribute() + * improved a deprecation message + +* 1.28.1 (2016-11-18) + + * fixed block() function when used with a template argument + +* 1.28.0 (2016-11-17) + + * added support for the PHP 7 null coalescing operator for the ?? Twig implementation + * exposed a way to access template data and methods in a portable way + * changed context access to use the PHP 7 null coalescing operator when available + * added the "with" tag + * added support for a custom template on the block() function + * added "is defined" support for block() and constant() + * optimized the way attributes are fetched + +* 1.27.0 (2016-10-25) + + * deprecated Twig_Parser::getEnvironment() + * deprecated Twig_Parser::addHandler() and Twig_Parser::addNodeVisitor() + * deprecated Twig_Compiler::addIndentation() + * fixed regression when registering two extensions having the same class name + * deprecated Twig_LoaderInterface::getSource() (implement Twig_SourceContextLoaderInterface instead) + * fixed the filesystem loader with relative paths + * deprecated Twig_Node::getLine() in favor of Twig_Node::getTemplateLine() + * deprecated Twig_Template::getSource() in favor of Twig_Template::getSourceContext() + * deprecated Twig_Node::getFilename() in favor of Twig_Node::getTemplateName() + * deprecated the "filename" escaping strategy (use "name" instead) + * added Twig_Source to hold information about the original template + * deprecated Twig_Error::getTemplateFile() and Twig_Error::setTemplateFile() in favor of Twig_Error::getTemplateName() and Twig_Error::setTemplateName() + * deprecated Parser::getFilename() + * fixed template paths when a template name contains a protocol like vfs:// + * improved debugging with Twig_Sandbox_SecurityError exceptions for disallowed methods and properties + +* 1.26.1 (2016-10-05) + + * removed template source code from generated template classes when debug is disabled + * fixed default implementation of Twig_Template::getDebugInfo() for better BC + * fixed regression on static calls for functions/filters/tests + +* 1.26.0 (2016-10-02) + + * added template cache invalidation based on more environment options + * added a missing deprecation notice + * fixed template paths when a template is stored in a PHAR file + * allowed filters/functions/tests implementation to use a different class than the extension they belong to + * deprecated Twig_ExtensionInterface::getName() + +* 1.25.0 (2016-09-21) + + * changed the way we store template source in template classes + * removed usage of realpath in cache keys + * fixed Twig cache sharing when used with different versions of PHP + * removed embed parent workaround for simple use cases + * deprecated the ability to store non Node instances in Node::$nodes + * deprecated Twig_Environment::getLexer(), Twig_Environment::getParser(), Twig_Environment::getCompiler() + * deprecated Twig_Compiler::getFilename() + +* 1.24.2 (2016-09-01) + + * fixed static callables + * fixed a potential PHP warning when loading the cache + * fixed a case where the autoescaping does not work as expected + +* 1.24.1 (2016-05-30) + + * fixed reserved keywords (forbids true, false, null and none keywords for variables names) + * fixed support for PHP7 (Throwable support) + * marked the following methods as being internals on Twig_Environment: + getFunctions(), getFilters(), getTests(), getFunction(), getFilter(), getTest(), + getTokenParsers(), getTags(), getNodeVisitors(), getUnaryOperators(), getBinaryOperators(), + getFunctions(), getFilters(), getGlobals(), initGlobals(), initExtensions(), and initExtension() + +* 1.24.0 (2016-01-25) + + * adding support for the ?? operator + * fixed the defined test when used on a constant, a map, or a sequence + * undeprecated _self (should only be used to get the template name, not the template instance) + * fixed parsing on PHP7 + +* 1.23.3 (2016-01-11) + + * fixed typo + +* 1.23.2 (2015-01-11) + + * added versions in deprecated messages + * made file cache tolerant for trailing (back)slashes on directory configuration + * deprecated unused Twig_Node_Expression_ExtensionReference class + +* 1.23.1 (2015-11-05) + + * fixed some exception messages which triggered PHP warnings + * fixed BC on Twig_Test_NodeTestCase + +* 1.23.0 (2015-10-29) + + * deprecated the possibility to override an extension by registering another one with the same name + * deprecated Twig_ExtensionInterface::getGlobals() (added Twig_Extension_GlobalsInterface for BC) + * deprecated Twig_ExtensionInterface::initRuntime() (added Twig_Extension_InitRuntimeInterface for BC) + * deprecated Twig_Environment::computeAlternatives() + +* 1.22.3 (2015-10-13) + + * fixed regression when using null as a cache strategy + * improved performance when checking template freshness + * fixed warnings when loaded templates do not exist + * fixed template class name generation to prevent possible collisions + * fixed logic for custom escapers to call them even on integers and null values + * changed template cache names to take into account the Twig C extension + +* 1.22.2 (2015-09-22) + + * fixed a race condition in template loading + +* 1.22.1 (2015-09-15) + + * fixed regression in template_from_string + +* 1.22.0 (2015-09-13) + + * made Twig_Test_IntegrationTestCase more flexible + * added an option to force PHP bytecode invalidation when writing a compiled template into the cache + * fixed the profiler duration for the root node + * changed template cache names to take into account enabled extensions + * deprecated Twig_Environment::clearCacheFiles(), Twig_Environment::getCacheFilename(), + Twig_Environment::writeCacheFile(), and Twig_Environment::getTemplateClassPrefix() + * added a way to override the filesystem template cache system + * added a way to get the original template source from Twig_Template + * 1.21.2 (2015-09-09) * fixed variable names for the deprecation triggering code @@ -20,7 +233,7 @@ * fixed limited RCEs when in sandbox mode * deprecated Twig_Template::getEnvironment() * deprecated the _self variable for usage outside of the from and import tags - * added Twig_BaseNodeVisitor to ease the compatibility of node visitors + * added Twig_BaseNodeVisitor to ease the compatibility of node visitors between 1.x and 2.x * 1.19.0 (2015-07-31) diff --git a/lib/silex/vendor/twig/twig/LICENSE b/lib/silex/vendor/twig/twig/LICENSE index a470002bf..b6e17a1a6 100644 --- a/lib/silex/vendor/twig/twig/LICENSE +++ b/lib/silex/vendor/twig/twig/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2009-2014 by the Twig Team. +Copyright (c) 2009-2017 by the Twig Team. Some rights reserved. diff --git a/lib/silex/vendor/twig/twig/composer.json b/lib/silex/vendor/twig/twig/composer.json index 2d3438ec2..09c1ea5e9 100644 --- a/lib/silex/vendor/twig/twig/composer.json +++ b/lib/silex/vendor/twig/twig/composer.json @@ -27,20 +27,24 @@ "forum": "https://groups.google.com/forum/#!forum/twig-users" }, "require": { - "php": ">=5.2.7" + "php": ">=5.3.3" }, "require-dev": { - "symfony/phpunit-bridge": "~2.7", - "symfony/debug": "~2.7" + "symfony/phpunit-bridge": "~3.3@dev", + "symfony/debug": "~2.7", + "psr/container": "^1.0" }, "autoload": { "psr-0" : { "Twig_" : "lib/" + }, + "psr-4" : { + "Twig\\" : "src/" } }, "extra": { "branch-alias": { - "dev-master": "1.21-dev" + "dev-master": "1.35-dev" } } } diff --git a/lib/silex/vendor/twig/twig/doc/advanced.rst b/lib/silex/vendor/twig/twig/doc/advanced.rst index a20eab034..d86db501e 100644 --- a/lib/silex/vendor/twig/twig/doc/advanced.rst +++ b/lib/silex/vendor/twig/twig/doc/advanced.rst @@ -136,7 +136,13 @@ Creating a filter is as simple as associating a name with a PHP callable:: // or a simple PHP function $filter = new Twig_SimpleFilter('rot13', 'str_rot13'); + // or a class static method + $filter = new Twig_SimpleFilter('rot13', array('SomeClass', 'rot13Filter')); + $filter = new Twig_SimpleFilter('rot13', 'SomeClass::rot13Filter'); + // or a class method + $filter = new Twig_SimpleFilter('rot13', array($this, 'rot13Filter')); + // the one below needs a runtime implementation (see below for more information) $filter = new Twig_SimpleFilter('rot13', array('SomeClass', 'rot13Filter')); The first argument passed to the ``Twig_SimpleFilter`` constructor is the name @@ -458,7 +464,7 @@ from the token stream (``$this->parser->getStream()``): type/value a syntax error is thrown. Otherwise, if the type and value are correct, the token is returned and the stream moves to the next token. -* ``look()``: Looks a the next token without consuming it. +* ``look()``: Looks at the next token without consuming it. Parsing expressions is done by calling the ``parseExpression()`` like we did for the ``set`` tag. @@ -525,10 +531,6 @@ reusable class like adding support for internationalization. An extension can define tags, filters, tests, operators, global variables, functions, and node visitors. -Creating an extension also makes for a better separation of code that is -executed at compilation time and code needed at runtime. As such, it makes -your code faster. - Most of the time, it is useful to create a single extension for your project, to host all the specific tags and filters you want to add to Twig. @@ -552,49 +554,49 @@ An extension is a class that implements the following interface:: * * This is where you can load some file that contains filter functions for instance. * - * @param Twig_Environment $environment The current Twig_Environment instance + * @deprecated since 1.23 (to be removed in 2.0), implement Twig_Extension_InitRuntimeInterface instead */ function initRuntime(Twig_Environment $environment); /** * Returns the token parser instances to add to the existing list. * - * @return array An array of Twig_TokenParserInterface or Twig_TokenParserBrokerInterface instances + * @return (Twig_TokenParserInterface|Twig_TokenParserBrokerInterface)[] */ function getTokenParsers(); /** * Returns the node visitor instances to add to the existing list. * - * @return array An array of Twig_NodeVisitorInterface instances + * @return Twig_NodeVisitorInterface[] */ function getNodeVisitors(); /** * Returns a list of filters to add to the existing list. * - * @return array An array of filters + * @return Twig_SimpleFilter[] */ function getFilters(); /** * Returns a list of tests to add to the existing list. * - * @return array An array of tests + * @return Twig_SimpleTest[] */ function getTests(); /** * Returns a list of functions to add to the existing list. * - * @return array An array of functions + * @return Twig_SimpleFunction[] */ function getFunctions(); /** * Returns a list of operators to add to the existing list. * - * @return array An array of operators + * @return array First array of unary operators, second array of binary operators */ function getOperators(); @@ -602,6 +604,8 @@ An extension is a class that implements the following interface:: * Returns a list of global variables to add to the existing list. * * @return array An array of global variables + * + * @deprecated since 1.23 (to be removed in 2.0), implement Twig_Extension_GlobalsInterface instead */ function getGlobals(); @@ -609,32 +613,27 @@ An extension is a class that implements the following interface:: * Returns the name of the extension. * * @return string The extension name + * + * @deprecated since 1.26 (to be removed in 2.0), not used anymore internally */ function getName(); } -To keep your extension class clean and lean, it can inherit from the built-in -``Twig_Extension`` class instead of implementing the whole interface. That -way, you just need to implement the ``getName()`` method as the -``Twig_Extension`` provides empty implementations for all other methods. - -The ``getName()`` method must return a unique identifier for your extension. - -Now, with this information in mind, let's create the most basic extension -possible:: +To keep your extension class clean and lean, inherit from the built-in +``Twig_Extension`` class instead of implementing the interface as it provides +empty implementations for all methods: class Project_Twig_Extension extends Twig_Extension { - public function getName() - { - return 'project'; - } } +Of course, this extension does nothing for now. We will customize it in the +next sections. + .. note:: - Of course, this extension does nothing for now. We will customize it in - the next sections. + Prior to Twig 1.26, you must implement the ``getName()`` method which must + return a unique identifier for the extension. Twig does not care where you save your extension on the filesystem, as all extensions must be registered explicitly to be available in your templates. @@ -645,12 +644,9 @@ main ``Environment`` object:: $twig = new Twig_Environment($loader); $twig->addExtension(new Project_Twig_Extension()); -Of course, you need to first load the extension file by either using -``require_once()`` or by using an autoloader (see `spl_autoload_register()`_). - .. tip:: - The bundled extensions are great examples of how extensions work. + The Twig core extensions are great examples of how extensions work. Globals ~~~~~~~ @@ -658,7 +654,7 @@ Globals Global variables can be registered in an extension via the ``getGlobals()`` method:: - class Project_Twig_Extension extends Twig_Extension + class Project_Twig_Extension extends Twig_Extension implements Twig_Extension_GlobalsInterface { public function getGlobals() { @@ -769,6 +765,106 @@ The ``getTests()`` method lets you add new test functions:: // ... } +Definition vs Runtime +~~~~~~~~~~~~~~~~~~~~~ + +Twig filters, functions, and tests runtime implementations can be defined as +any valid PHP callable: + +* **functions/static methods**: Simple to implement and fast (used by all Twig + core extensions); but it is hard for the runtime to depend on external + objects; + +* **closures**: Simple to implement; + +* **object methods**: More flexible and required if your runtime code depends + on external objects. + +The simplest way to use methods is to define them on the extension itself:: + + class Project_Twig_Extension extends Twig_Extension + { + private $rot13Provider; + + public function __construct($rot13Provider) + { + $this->rot13Provider = $rot13Provider; + } + + public function getFunctions() + { + return array( + new Twig_SimpleFunction('rot13', array($this, 'rot13')), + ); + } + + public function rot13($value) + { + return $rot13Provider->rot13($value); + } + } + +This is very convenient but not recommended as it makes template compilation +depend on runtime dependencies even if they are not needed (think for instance +as a dependency that connects to a database engine). + +As of Twig 1.26, you can easily decouple the extension definitions from their +runtime implementations by registering a ``Twig_RuntimeLoaderInterface`` +instance on the environment that knows how to instantiate such runtime classes +(runtime classes must be autoload-able):: + + class RuntimeLoader implements Twig_RuntimeLoaderInterface + { + public function load($class) + { + // implement the logic to create an instance of $class + // and inject its dependencies + // most of the time, it means using your dependency injection container + if ('Project_Twig_RuntimeExtension' === $class) { + return new $class(new Rot13Provider()); + } else { + // ... + } + } + } + + $twig->addRuntimeLoader(new RuntimeLoader()); + +.. note:: + + As of Twig 1.32, Twig comes with a PSR-11 compatible runtime loader + (``Twig_ContainerRuntimeLoader``) that works on PHP 5.3+. + +It is now possible to move the runtime logic to a new +``Project_Twig_RuntimeExtension`` class and use it directly in the extension:: + + class Project_Twig_RuntimeExtension + { + private $rot13Provider; + + public function __construct($rot13Provider) + { + $this->rot13Provider = $rot13Provider; + } + + public function rot13($value) + { + return $rot13Provider->rot13($value); + } + } + + class Project_Twig_Extension extends Twig_Extension + { + public function getFunctions() + { + return array( + new Twig_SimpleFunction('rot13', array('Project_Twig_RuntimeExtension', 'rot13')), + // or + new Twig_SimpleFunction('rot13', 'Project_Twig_RuntimeExtension::rot13'), + ); + } + } + Overloading ----------- @@ -789,11 +885,6 @@ possible** (order matters):: { // do something different from the built-in date filter } - - public function getName() - { - return 'project'; - } } $twig = new Twig_Environment($loader); @@ -801,7 +892,7 @@ possible** (order matters):: Here, we have overloaded the built-in ``date`` filter with a custom one. -If you do the same on the Twig_Environment itself, beware that it takes +If you do the same on the ``Twig_Environment`` itself, beware that it takes precedence over any other registered extensions:: $twig = new Twig_Environment($loader); @@ -866,7 +957,6 @@ Testing the node visitors can be complex, so extend your test cases from ``Twig_Test_NodeTestCase``. Examples can be found in the Twig repository `tests/Twig/Node`_ directory. -.. _`spl_autoload_register()`: http://www.php.net/spl_autoload_register .. _`rot13`: http://www.php.net/manual/en/function.str-rot13.php .. _`tests/Twig/Fixtures`: https://github.com/twigphp/Twig/tree/master/test/Twig/Tests/Fixtures .. _`tests/Twig/Node`: https://github.com/twigphp/Twig/tree/master/test/Twig/Tests/Node diff --git a/lib/silex/vendor/twig/twig/doc/advanced_legacy.rst b/lib/silex/vendor/twig/twig/doc/advanced_legacy.rst index 2ef6bfde8..33e9f45a4 100644 --- a/lib/silex/vendor/twig/twig/doc/advanced_legacy.rst +++ b/lib/silex/vendor/twig/twig/doc/advanced_legacy.rst @@ -539,50 +539,48 @@ An extension is a class that implements the following interface:: * Initializes the runtime environment. * * This is where you can load some file that contains filter functions for instance. - * - * @param Twig_Environment $environment The current Twig_Environment instance */ function initRuntime(Twig_Environment $environment); /** * Returns the token parser instances to add to the existing list. * - * @return array An array of Twig_TokenParserInterface or Twig_TokenParserBrokerInterface instances + * @return (Twig_TokenParserInterface|Twig_TokenParserBrokerInterface)[] */ function getTokenParsers(); /** * Returns the node visitor instances to add to the existing list. * - * @return array An array of Twig_NodeVisitorInterface instances + * @return Twig_NodeVisitorInterface[] */ function getNodeVisitors(); /** * Returns a list of filters to add to the existing list. * - * @return array An array of filters + * @return Twig_SimpleFilter[] */ function getFilters(); /** * Returns a list of tests to add to the existing list. * - * @return array An array of tests + * @return Twig_SimpleTest[] */ function getTests(); /** * Returns a list of functions to add to the existing list. * - * @return array An array of functions + * @return Twig_SimpleFunction[] */ function getFunctions(); /** * Returns a list of operators to add to the existing list. * - * @return array An array of operators + * @return array First array of unary operators, second array of binary operators */ function getOperators(); diff --git a/lib/silex/vendor/twig/twig/doc/api.rst b/lib/silex/vendor/twig/twig/doc/api.rst index f367db07a..8be2b8af8 100644 --- a/lib/silex/vendor/twig/twig/doc/api.rst +++ b/lib/silex/vendor/twig/twig/doc/api.rst @@ -43,10 +43,18 @@ templates from a database or other resources. the evaluated templates. For such a need, you can use any available PHP cache library. -To load a template from this environment you just have to call the -``loadTemplate()`` method which then returns a ``Twig_Template`` instance:: +Rendering Templates +------------------- - $template = $twig->loadTemplate('index.html'); +To load a template from a Twig environment, call the ``load()`` method which +returns a ``Twig_TemplateWrapper`` instance:: + + $template = $twig->load('index.html'); + +.. note:: + + Before Twig 1.28, you should use ``loadTemplate()`` instead which returns a + ``Twig_Template`` instance. To render the template with some variables, call the ``render()`` method:: @@ -60,6 +68,14 @@ You can also load and render the template in one fell swoop:: echo $twig->render('index.html', array('the' => 'variables', 'go' => 'here')); +.. versionadded:: 1.28 + The possibility to render blocks from the API was added in Twig 1.28. + +If a template defines blocks, they can be rendered individually via the +``renderBlock()`` call:: + + echo $template->renderBlock('block_name', array('the' => 'variables', 'go' => 'here')); + .. _environment_options: Environment Options @@ -78,16 +94,16 @@ The following options are available: ``__toString()`` method that you can use to display the generated nodes (default to ``false``). -* ``charset`` *string (default to ``utf-8``)* +* ``charset`` *string* (defaults to ``utf-8``) The charset used by the templates. -* ``base_template_class`` *string (default to ``Twig_Template``)* +* ``base_template_class`` *string* (defaults to ``Twig_Template``) The base template class to use for generated templates. -* ``cache`` *string|false* +* ``cache`` *string* or ``false`` An absolute path where to store the compiled templates, or ``false`` to disable caching (which is the default). @@ -106,7 +122,7 @@ The following options are available: replace them with a ``null`` value. When set to ``true``, Twig throws an exception instead (default to ``false``). -* ``autoescape`` *string|boolean* +* ``autoescape`` *string* or *boolean* If set to ``true``, HTML auto-escaping will be enabled by default for all templates (default to ``true``). @@ -115,14 +131,14 @@ The following options are available: ``false`` to disable). As of Twig 1.9, you can set the escaping strategy to use (``css``, ``url``, - ``html_attr``, or a PHP callback that takes the template "filename" and must + ``html_attr``, or a PHP callback that takes the template name and must return the escaping strategy to use -- the callback cannot be a function name to avoid collision with built-in escaping strategies). - As of Twig 1.17, the ``filename`` escaping strategy determines the escaping - strategy to use for a template based on the template filename extension (this - strategy does not incur any overhead at runtime as auto-escaping is done at - compilation time.) + As of Twig 1.17, the ``filename`` escaping strategy (renamed to ``name`` as + of Twig 1.27) determines the escaping strategy to use for a template based on + the template filename extension (this strategy does not incur any overhead at + runtime as auto-escaping is done at compilation time.) * ``optimizations`` *integer* @@ -156,6 +172,9 @@ Here is a list of the built-in loaders Twig provides: .. versionadded:: 1.10 The ``prependPath()`` and support for namespaces were added in Twig 1.10. +.. versionadded:: 1.27 + Relative paths support was added in Twig 1.27. + ``Twig_Loader_Filesystem`` loads templates from the file system. This loader can find templates in folders on the file system and is the preferred way to load them:: @@ -190,6 +209,18 @@ Namespaced templates can be accessed via the special $twig->render('@admin/index.html', array()); +``Twig_Loader_Filesystem`` support absolute and relative paths. Using relative +paths is preferred as it makes the cache keys independent of the project root +directory (for instance, it allows warming the cache from a build server where +the directory might be different from the one used on production servers):: + + $loader = new Twig_Loader_Filesystem('templates', getcwd().'/..'); + +.. note:: + + When not passing the root path as a second argument, Twig uses ``getcwd()`` + for relative paths. + ``Twig_Loader_Array`` ..................... @@ -256,6 +287,8 @@ All loaders implement the ``Twig_LoaderInterface``:: * @param string $name string The name of the template to load * * @return string The template source code + * + * @deprecated since 1.27 (to be removed in 2.0), implement Twig_SourceContextLoaderInterface */ function getSource($name); @@ -280,6 +313,11 @@ All loaders implement the ``Twig_LoaderInterface``:: The ``isFresh()`` method must return ``true`` if the current cached template is still fresh, given the last modification time, or ``false`` otherwise. +.. note:: + + As of Twig 1.27, you should also implement + ``Twig_SourceContextLoaderInterface`` to avoid deprecation notices. + .. tip:: As of Twig 1.11.0, you can also implement ``Twig_ExistsLoaderInterface`` diff --git a/lib/silex/vendor/twig/twig/doc/coding_standards.rst b/lib/silex/vendor/twig/twig/doc/coding_standards.rst index f435df490..bf8ea91a4 100644 --- a/lib/silex/vendor/twig/twig/doc/coding_standards.rst +++ b/lib/silex/vendor/twig/twig/doc/coding_standards.rst @@ -95,7 +95,7 @@ standards: .. code-block:: jinja {% block foo %} - {% if true %} - true - {% endif %} + {% if true %} + true + {% endif %} {% endblock %} diff --git a/lib/silex/vendor/twig/twig/doc/deprecated.rst b/lib/silex/vendor/twig/twig/doc/deprecated.rst index 1489174a6..8b25cc410 100644 --- a/lib/silex/vendor/twig/twig/doc/deprecated.rst +++ b/lib/silex/vendor/twig/twig/doc/deprecated.rst @@ -11,6 +11,13 @@ Deprecation Notices As of Twig 1.21, Twig generates deprecation notices when a template uses deprecated features. See :ref:`deprecation-notices` for more information. +Macros +------ + +As of Twig 2.0, macros imported in a file are not available in child templates +anymore (via an ``include`` call for instance). You need to import macros +explicitly in each file where you are using them. + Token Parsers ------------- @@ -20,12 +27,31 @@ Token Parsers * ``Twig_TokenParserBrokerInterface`` * ``Twig_TokenParserBroker`` +* As of Twig 1.27, ``Twig_Parser::getFilename()`` is deprecated. From a token + parser, use ``$this->parser->getStream()->getSourceContext()->getPath()`` instead. + +* As of Twig 1.27, ``Twig_Parser::getEnvironment()`` is deprecated. + Extensions ---------- * As of Twig 1.x, the ability to remove an extension is deprecated and the ``Twig_Environment::removeExtension()`` method will be removed in 2.0. +* As of Twig 1.23, the ``Twig_ExtensionInterface::initRuntime()`` method is + deprecated. You have two options to avoid the deprecation notice: if you + implement this method to store the environment for your custom filters, + functions, or tests, use the ``needs_environment`` option instead; if you + have more complex needs, explicitly implement + ``Twig_Extension_InitRuntimeInterface`` (not recommended). + +* As of Twig 1.23, the ``Twig_ExtensionInterface::getGlobals()`` method is + deprecated. Implement ``Twig_Extension_GlobalsInterface`` to avoid + deprecation notices. + +* As of Twig 1.26, the ``Twig_ExtensionInterface::getName()`` method is + deprecated and it is not used internally anymore. + PEAR ---- @@ -98,6 +124,17 @@ Nodes * As of Twig 1.x, ``Node::toXml()`` is deprecated and will be removed in Twig 2.0. +* As of Twig 1.26, ``Node::$nodes`` should only contains ``Twig_Node`` + instances, storing a ``null`` value is deprecated and won't be possible in + Twig 2.x. + +* As of Twig 1.27, the ``filename`` attribute on ``Twig_Node_Module`` is + deprecated. Use ``getName()`` instead. + +* As of Twig 1.27, the ``Twig_Node::getFilename()/Twig_Node::getLine()`` + methods are deprecated, use + ``Twig_Node::getTemplateName()/Twig_Node::getTemplateLine()`` instead. + Interfaces ---------- @@ -109,21 +146,35 @@ Interfaces * ``Twig_NodeInterface`` (use ``Twig_Node`` instead) * ``Twig_ParserInterface`` (use ``Twig_Parser`` instead) * ``Twig_ExistsLoaderInterface`` (merged with ``Twig_LoaderInterface``) +* ``Twig_SourceContextLoaderInterface`` (merged with ``Twig_LoaderInterface``) * ``Twig_TemplateInterface`` (use ``Twig_Template`` instead, and use those constants Twig_Template::ANY_CALL, Twig_Template::ARRAY_CALL, Twig_Template::METHOD_CALL) +Compiler +-------- + +* As of Twig 1.26, the ``Twig_Compiler::getFilename()`` has been deprecated. + You should not use it anyway as its values is not reliable. + +* As of Twig 1.27, the ``Twig_Compiler::addIndentation()`` has been deprecated. + Use ``Twig_Compiler::write('')`` instead. + Loaders ------- * As of Twig 1.x, ``Twig_Loader_String`` is deprecated and will be removed in - 2.0. + 2.0. You can render a string via ``Twig_Environment::createTemplate()``. + +* As of Twig 1.27, ``Twig_LoaderInterface::getSource()`` is deprecated. + Implement ``Twig_SourceContextLoaderInterface`` instead and use + ``getSourceContext()``. Node Visitors ------------- * Because of the removal of ``Twig_NodeInterface`` in 2.0, you need to extend - ``Twig_BaseNodeVistor`` instead of implementing ``Twig_NodeVisitorInterface`` + ``Twig_BaseNodeVisitor`` instead of implementing ``Twig_NodeVisitorInterface`` directly to make your node visitors compatible with both Twig 1.x and 2.x. Globals @@ -133,16 +184,41 @@ Globals or the extensions have been initialized is not possible anymore (but changing the value of an already registered global is possible). -* As of Twig 1.x, the ``_self`` global variable is deprecated except for usage - in the ``from`` and the ``import`` tags. In Twig 2.0, ``_self`` is not - exposed anymore but still usable in the ``from`` and the ``import`` tags. +* As of Twig 1.x, using the ``_self`` global variable to get access to the + current ``Twig_Template`` instance is deprecated; most usages only need the + current template name, which will continue to work in Twig 2.0. In Twig 2.0, + ``_self`` returns the current template name instead of the current + ``Twig_Template`` instance. If you are using ``{{ _self.templateName }}``, + just replace it with ``{{ _self }}``. Miscellaneous ------------- -* As of Twig 1.x, ``Twig_Environment::clearTemplateCache()`` is deprecated and - will be removed in 2.0. +* As of Twig 1.x, ``Twig_Environment::clearTemplateCache()``, + ``Twig_Environment::writeCacheFile()``, + ``Twig_Environment::clearCacheFiles()``, + ``Twig_Environment::getCacheFilename()``, + ``Twig_Environment::getTemplateClassPrefix()``, + ``Twig_Environment::getLexer()``, ``Twig_Environment::getParser()``, and + ``Twig_Environment::getCompiler()`` are deprecated and will be removed in 2.0. * As of Twig 1.x, ``Twig_Template::getEnvironment()`` and ``Twig_TemplateInterface::getEnvironment()`` are deprecated and will be removed in 2.0. + +* As of Twig 1.21, setting the environment option ``autoescape`` to ``true`` is + deprecated and will be removed in 2.0. Use ``"html"`` instead. + +* As of Twig 1.27, ``Twig_Error::getTemplateFile()`` and + ``Twig_Error::setTemplateFile()`` are deprecated. Use + ``Twig_Error::getTemplateName()`` and ``Twig_Error::setTemplateName()`` + instead. + +* As of Twig 1.27, ``Twig_Template::getSource()`` is deprecated. Use + ``Twig_Template::getSourceContext()`` instead. + +* As of Twig 1.27, ``Twig_Parser::addHandler()`` and + ``Twig_Parser::addNodeVisitor()`` are deprecated and will be removed in 2.0. + +* As of Twig 1.29, some classes are marked as being final via the `@final` + annotation. Those classes will be marked as final in 2.0. diff --git a/lib/silex/vendor/twig/twig/doc/filters/date.rst b/lib/silex/vendor/twig/twig/doc/filters/date.rst index c86d42baf..99a17ab75 100644 --- a/lib/silex/vendor/twig/twig/doc/filters/date.rst +++ b/lib/silex/vendor/twig/twig/doc/filters/date.rst @@ -54,6 +54,9 @@ dates and the second one is the default format for date intervals: .. code-block:: php $twig = new Twig_Environment($loader); + $twig->getExtension('Twig_Extension_Core')->setDateFormat('d/m/Y', '%d days'); + + // before Twig 1.26 $twig->getExtension('core')->setDateFormat('d/m/Y', '%d days'); Timezone @@ -79,6 +82,9 @@ The default timezone can also be set globally by calling ``setTimezone()``: .. code-block:: php $twig = new Twig_Environment($loader); + $twig->getExtension('Twig_Extension_Core')->setTimezone('Europe/Paris'); + + // before Twig 1.26 $twig->getExtension('core')->setTimezone('Europe/Paris'); Arguments diff --git a/lib/silex/vendor/twig/twig/doc/filters/escape.rst b/lib/silex/vendor/twig/twig/doc/filters/escape.rst index fc9771acd..21491343b 100644 --- a/lib/silex/vendor/twig/twig/doc/filters/escape.rst +++ b/lib/silex/vendor/twig/twig/doc/filters/escape.rst @@ -97,7 +97,10 @@ used in the ``escape`` call) and the second one must be a valid PHP callable: .. code-block:: php $twig = new Twig_Environment($loader); - $twig->getExtension('core')->setEscaper('csv', 'csv_escaper')); + $twig->getExtension('Twig_Extension_Core')->setEscaper('csv', 'csv_escaper'); + + // before Twig 1.26 + $twig->getExtension('core')->setEscaper('csv', 'csv_escaper'); When called by Twig, the callable receives the Twig environment instance, the string to escape, and the charset. diff --git a/lib/silex/vendor/twig/twig/doc/filters/length.rst b/lib/silex/vendor/twig/twig/doc/filters/length.rst index 1f783b3dd..5620f3b3c 100644 --- a/lib/silex/vendor/twig/twig/doc/filters/length.rst +++ b/lib/silex/vendor/twig/twig/doc/filters/length.rst @@ -1,8 +1,18 @@ ``length`` ========== +.. versionadded:: 1.33 + + Support for the ``__toString()`` magic method has been added in Twig 1.33. + The ``length`` filter returns the number of items of a sequence or mapping, or -the length of a string: +the length of a string. + +For objects that implement the ``Countable`` interface, ``length`` will use the +return value of the ``count()`` method. + +For objects that implement the ``__toString()`` magic method (and not ``Countable``), +it will return the length of the string provided by that method. .. code-block:: jinja diff --git a/lib/silex/vendor/twig/twig/doc/filters/number_format.rst b/lib/silex/vendor/twig/twig/doc/filters/number_format.rst index 3114e8451..f9d6705e7 100644 --- a/lib/silex/vendor/twig/twig/doc/filters/number_format.rst +++ b/lib/silex/vendor/twig/twig/doc/filters/number_format.rst @@ -18,6 +18,14 @@ separator using the additional arguments: {{ 9800.333|number_format(2, '.', ',') }} +To format negative numbers, wrap the number with parentheses (needed because of +Twig's :ref:`precedence of operators `: + +.. code-block:: jinja + + {{ -9800.333|number_format(2, '.', ',') }} {# outputs : -9 #} + {{ (-9800.333)|number_format(2, '.', ',') }} {# outputs : -9,800.33 #} + If no formatting options are provided then Twig will use the default formatting options of: @@ -30,6 +38,9 @@ These defaults can be easily changed through the core extension: .. code-block:: php $twig = new Twig_Environment($loader); + $twig->getExtension('Twig_Extension_Core')->setNumberFormat(3, '.', ','); + + // before Twig 1.26 $twig->getExtension('core')->setNumberFormat(3, '.', ','); The defaults set for ``number_format`` can be over-ridden upon each call using the diff --git a/lib/silex/vendor/twig/twig/doc/filters/replace.rst b/lib/silex/vendor/twig/twig/doc/filters/replace.rst index 1227957b9..8dbb74593 100644 --- a/lib/silex/vendor/twig/twig/doc/filters/replace.rst +++ b/lib/silex/vendor/twig/twig/doc/filters/replace.rst @@ -14,6 +14,6 @@ The ``replace`` filter formats a given string by replacing the placeholders Arguments --------- -* ``replace_pairs``: The placeholder values +* ``from``: The placeholder values .. seealso:: :doc:`format` diff --git a/lib/silex/vendor/twig/twig/doc/filters/striptags.rst b/lib/silex/vendor/twig/twig/doc/filters/striptags.rst index 72c6f252f..82953b7b6 100644 --- a/lib/silex/vendor/twig/twig/doc/filters/striptags.rst +++ b/lib/silex/vendor/twig/twig/doc/filters/striptags.rst @@ -8,8 +8,22 @@ by one space: {{ some_html|striptags }} +You can also provide tags which should not be stripped: + +.. code-block:: jinja + + {{ some_html|striptags('

') }} + +In this example, the ``
``, ``
``, ``

``, and ``

`` tags won't be +removed from the string. + .. note:: Internally, Twig uses the PHP `strip_tags`_ function. +Arguments +--------- + +* ``allowable_tags``: Tags which should not be stripped + .. _`strip_tags`: http://php.net/strip_tags diff --git a/lib/silex/vendor/twig/twig/doc/filters/trim.rst b/lib/silex/vendor/twig/twig/doc/filters/trim.rst index 4ddb2083b..b598363c2 100644 --- a/lib/silex/vendor/twig/twig/doc/filters/trim.rst +++ b/lib/silex/vendor/twig/twig/doc/filters/trim.rst @@ -1,6 +1,9 @@ ``trim`` ======== +.. versionadded:: 1.32 + The ``side`` argument was added in Twig 1.32. + .. versionadded:: 1.6.2 The ``trim`` filter was added in Twig 1.6.2. @@ -17,13 +20,26 @@ and end of a string: {# outputs ' I like Twig' #} + {{ ' I like Twig. '|trim(side='left') }} + + {# outputs 'I like Twig. ' #} + + {{ ' I like Twig. '|trim(' ', 'right') }} + + {# outputs ' I like Twig.' #} + .. note:: - Internally, Twig uses the PHP `trim`_ function. + Internally, Twig uses the PHP `trim`_, `ltrim`_, and `rtrim`_ functions. Arguments --------- * ``character_mask``: The characters to strip +* ``side``: The default is to strip from the left and the right (`both`) sides, but `left` + and `right` will strip from either the left side or right side only + .. _`trim`: http://php.net/trim +.. _`ltrim`: http://php.net/ltrim +.. _`rtrim`: http://php.net/rtrim diff --git a/lib/silex/vendor/twig/twig/doc/functions/block.rst b/lib/silex/vendor/twig/twig/doc/functions/block.rst index fd571efbc..f5d683c73 100644 --- a/lib/silex/vendor/twig/twig/doc/functions/block.rst +++ b/lib/silex/vendor/twig/twig/doc/functions/block.rst @@ -1,6 +1,12 @@ ``block`` ========= +.. versionadded: 1.28 + Using ``block`` with the ``defined`` test was added in Twig 1.28. + +.. versionadded: 1.28 + Support for the template argument was added in Twig 1.28. + When a template uses inheritance and if you want to print a block multiple times, use the ``block`` function: @@ -12,4 +18,24 @@ times, use the ``block`` function: {% block body %}{% endblock %} +The ``block`` function can also be used to display one block of another +template: + +.. code-block:: jinja + + {{ block("title", "common_blocks.twig") }} + +Use the ``defined`` test to check if a block exists in the context of the +current template: + +.. code-block:: jinja + + {% if block("footer") is defined %} + ... + {% endif %} + + {% if block("footer", "common_blocks.twig") is defined %} + ... + {% endif %} + .. seealso:: :doc:`extends<../tags/extends>`, :doc:`parent<../functions/parent>` diff --git a/lib/silex/vendor/twig/twig/doc/functions/constant.rst b/lib/silex/vendor/twig/twig/doc/functions/constant.rst index bea0e9fc0..97aa5c8f3 100644 --- a/lib/silex/vendor/twig/twig/doc/functions/constant.rst +++ b/lib/silex/vendor/twig/twig/doc/functions/constant.rst @@ -4,6 +4,9 @@ .. versionadded: 1.12.1 constant now accepts object instances as the second argument. +.. versionadded: 1.28 + Using ``constant`` with the ``defined`` test was added in Twig 1.28. + ``constant`` returns the constant value for a given string: .. code-block:: jinja @@ -16,3 +19,11 @@ As of 1.12.1 you can read constants from object instances as well: .. code-block:: jinja {{ constant('RSS', date) }} + +Use the ``defined`` test to check if a constant is defined: + +.. code-block:: jinja + + {% if constant('SOME_CONST') is defined %} + ... + {% endif %} diff --git a/lib/silex/vendor/twig/twig/doc/functions/date.rst b/lib/silex/vendor/twig/twig/doc/functions/date.rst index 714e08c47..158dd6a65 100644 --- a/lib/silex/vendor/twig/twig/doc/functions/date.rst +++ b/lib/silex/vendor/twig/twig/doc/functions/date.rst @@ -41,6 +41,9 @@ If no argument is passed, the function returns the current date: .. code-block:: php $twig = new Twig_Environment($loader); + $twig->getExtension('Twig_Extension_Core')->setTimezone('Europe/Paris'); + + // before Twig 1.26 $twig->getExtension('core')->setTimezone('Europe/Paris'); Arguments diff --git a/lib/silex/vendor/twig/twig/doc/functions/include.rst b/lib/silex/vendor/twig/twig/doc/functions/include.rst index 33bd56d11..2f88ed776 100644 --- a/lib/silex/vendor/twig/twig/doc/functions/include.rst +++ b/lib/silex/vendor/twig/twig/doc/functions/include.rst @@ -37,14 +37,18 @@ You can disable access to the context by setting ``with_context`` to {# no variables will be accessible #} {{ include('template.html', with_context = false) }} -And if the expression evaluates to a ``Twig_Template`` object, Twig will use it -directly:: +And if the expression evaluates to a ``Twig_Template`` or a +``Twig_TemplateWrapper`` instance, Twig will use it directly:: // {{ include(template) }} + // deprecated as of Twig 1.28 $template = $twig->loadTemplate('some_template.twig'); - $twig->loadTemplate('template.twig')->display(array('template' => $template)); + // as of Twig 1.28 + $template = $twig->load('some_template.twig'); + + $twig->display('template.twig', array('template' => $template)); When you set the ``ignore_missing`` flag, Twig will return an empty string if the template does not exist: diff --git a/lib/silex/vendor/twig/twig/doc/functions/range.rst b/lib/silex/vendor/twig/twig/doc/functions/range.rst index b7cd01116..5c9db0892 100644 --- a/lib/silex/vendor/twig/twig/doc/functions/range.rst +++ b/lib/silex/vendor/twig/twig/doc/functions/range.rst @@ -12,7 +12,7 @@ Returns a list containing an arithmetic progression of integers: {# outputs 0, 1, 2, 3, #} When step is given (as the third parameter), it specifies the increment (or -decrement): +decrement for negative values): .. code-block:: jinja @@ -22,8 +22,21 @@ decrement): {# outputs 0, 2, 4, 6, #} +.. note:: + + Note that if the start is greater than the end, ``range`` assumes a step of + ``-1``: + + .. code-block:: jinja + + {% for i in range(3, 0) %} + {{ i }}, + {% endfor %} + + {# outputs 3, 2, 1, 0, #} + The Twig built-in ``..`` operator is just syntactic sugar for the ``range`` -function (with a step of 1): +function (with a step of ``1``, or ``-1`` if the start is greater than the end): .. code-block:: jinja diff --git a/lib/silex/vendor/twig/twig/doc/internals.rst b/lib/silex/vendor/twig/twig/doc/internals.rst index ef1174dd9..fec65e00e 100644 --- a/lib/silex/vendor/twig/twig/doc/internals.rst +++ b/lib/silex/vendor/twig/twig/doc/internals.rst @@ -44,7 +44,11 @@ an instance of ``Twig_Token``, and the stream is an instance of You can manually convert a source code into a token stream by calling the ``tokenize()`` method of an environment:: - $stream = $twig->tokenize($source, $identifier); + $stream = $twig->tokenize(new Twig_Source($source, $identifier)); + +.. versionadded:: 1.27 + ``Twig_Source`` was introduced in version 1.27, pass the source and the + identifier directly on previous versions. As the stream has a ``__toString()`` method, you can have a textual representation of it by echoing the object:: @@ -124,7 +128,7 @@ using):: { // line 1 echo "Hello "; - echo twig_escape_filter($this->env, isset($context["name"]) ? $context["name"] : null), "html", null, true); + echo twig_escape_filter($this->env, (isset($context["name"]) ? $context["name"] : null), "html", null, true); } // some more code diff --git a/lib/silex/vendor/twig/twig/doc/intro.rst b/lib/silex/vendor/twig/twig/doc/intro.rst index 9b38c97d4..d631430f7 100644 --- a/lib/silex/vendor/twig/twig/doc/intro.rst +++ b/lib/silex/vendor/twig/twig/doc/intro.rst @@ -19,16 +19,17 @@ The key-features are... may modify the template design. * *Flexible*: Twig is powered by a flexible lexer and parser. This allows the - developer to define its own custom tags and filters, and create its own DSL. + developer to define their own custom tags and filters, and to create their own DSL. Twig is used by many Open-Source projects like Symfony, Drupal8, eZPublish, -phpBB, Piwik, OroCRM, and many frameworks have support for it as well like -Slim, Yii, Laravel, Codeigniter, and Kohana, just to name a few. +phpBB, Piwik, OroCRM; and many frameworks have support for it as well like +Slim, Yii, Laravel, Codeigniter and Kohana — just to name a few. Prerequisites ------------- -Twig needs at least **PHP 5.2.7** to run. +Twig needs at least **PHP 5.2.7** to run. As of 1.34, the minimum requirement +was bumped to **PHP 5.3.3**. Installation ------------ diff --git a/lib/silex/vendor/twig/twig/doc/recipes.rst b/lib/silex/vendor/twig/twig/doc/recipes.rst index 450ce558f..b3ba7f4d7 100644 --- a/lib/silex/vendor/twig/twig/doc/recipes.rst +++ b/lib/silex/vendor/twig/twig/doc/recipes.rst @@ -30,7 +30,7 @@ catches deprecation notices, and return them. .. tip:: If your templates are not stored on the filesystem, use the ``collect()`` - method instead which takes an ``Iterator``; the iterator must return + method instead. ``collect()`` takes a ``Traversable`` which must return template names as keys and template contents as values (as done by ``Twig_Util_TemplateDirIterator``). @@ -280,7 +280,7 @@ For functions, use ``registerUndefinedFunctionCallback()``:: // don't try this at home as it's not secure at all! $twig->registerUndefinedFunctionCallback(function ($name) { if (function_exists($name)) { - return new Twig_Function_Function($name); + return new Twig_SimpleFunction($name, $name); } return false; @@ -306,7 +306,7 @@ saving it. If the template code is stored in a `$template` variable, here is how you can do it:: try { - $twig->parse($twig->tokenize($template)); + $twig->parse($twig->tokenize(new Twig_Source($template))); // the $template is valid } catch (Twig_Error_Syntax $e) { @@ -318,7 +318,7 @@ If you iterate over a set of files, you can pass the filename to the foreach ($files as $file) { try { - $twig->parse($twig->tokenize($template, $file)); + $twig->parse($twig->tokenize(new Twig_Source($template, $file->getFilename(), $file))); // the $template is valid } catch (Twig_Error_Syntax $e) { @@ -326,6 +326,10 @@ If you iterate over a set of files, you can pass the filename to the } } +.. versionadded:: 1.27 + ``Twig_Source`` was introduced in version 1.27, pass the source and the + identifier directly on previous versions. + .. note:: This method won't catch any sandbox policy violations because the policy @@ -337,24 +341,33 @@ Refreshing modified Templates when OPcache or APC is enabled When using OPcache with ``opcache.validate_timestamps`` set to ``0`` or APC with ``apc.stat`` set to ``0`` and Twig cache enabled, clearing the template -cache won't update the cache. To get around this, one can extend -``Twig_Environment`` and force the update of the cache when Twig rewrites the -cache:: +cache won't update the cache. - class Twig_Environment_APC extends Twig_Environment - { - protected function writeCacheFile($file, $content) +To get around this, force Twig to invalidate the bytecode cache:: + + $twig = new Twig_Environment($loader, array( + 'cache' => new Twig_Cache_Filesystem('/some/cache/path', Twig_Cache_Filesystem::FORCE_BYTECODE_INVALIDATION), + // ... + )); + +.. note:: + + Before Twig 1.22, you should extend ``Twig_Environment`` instead:: + + class OpCacheAwareTwigEnvironment extends Twig_Environment { - parent::writeCacheFile($file, $content); + protected function writeCacheFile($file, $content) + { + parent::writeCacheFile($file, $content); - // Compile cached file into bytecode cache - if (extension_loaded('Zend OPcache') && ini_get('opcache.enable')) { - opcache_invalidate($file); - } elseif (extension_loaded('apc') && ini_get('apc.enabled')) { - apc_compile_file($file); + // Compile cached file into bytecode cache + if (function_exists('opcache_invalidate')) { + opcache_invalidate($file, true); + } elseif (function_exists('apc_compile_file')) { + apc_compile_file($file); + } } } - } Reusing a stateful Node Visitor ------------------------------- @@ -404,7 +417,7 @@ We have created a simple ``templates`` table that hosts two templates: Now, let's define a loader able to use this database:: - class DatabaseTwigLoader implements Twig_LoaderInterface, Twig_ExistsLoaderInterface + class DatabaseTwigLoader implements Twig_LoaderInterface, Twig_ExistsLoaderInterface, Twig_SourceContextLoaderInterface { protected $dbh; @@ -422,6 +435,16 @@ Now, let's define a loader able to use this database:: return $source; } + // Twig_SourceContextLoaderInterface as of Twig 1.27 + public function getSourceContext($name) + { + if (false === $source = $this->getValue('source', $name)) { + throw new Twig_Error_Loader(sprintf('Template "%s" does not exist.', $name)); + } + + return new Twig_Source($source, $name); + } + // Twig_ExistsLoaderInterface as of Twig 1.11 public function exists($name) { @@ -485,4 +508,61 @@ logical name, and not the path from the filesystem:: Now that the ``base.twig`` templates is defined in an array loader, you can remove it from the database, and everything else will still work as before. +Loading a Template from a String +-------------------------------- + +From a template, you can easily load a template stored in a string via the +``template_from_string`` function (available as of Twig 1.11 via the +``Twig_Extension_StringLoader`` extension): + +.. code-block:: jinja + + {{ include(template_from_string("Hello {{ name }}")) }} + +From PHP, it's also possible to load a template stored in a string via +``Twig_Environment::createTemplate()`` (available as of Twig 1.18):: + + $template = $twig->createTemplate('hello {{ name }}'); + echo $template->render(array('name' => 'Fabien')); + +.. note:: + + Never use the ``Twig_Loader_String`` loader, which has severe limitations. + +Using Twig and AngularJS in the same Templates +---------------------------------------------- + +Mixing different template syntaxes in the same file is not a recommended +practice as both AngularJS and Twig use the same delimiters in their syntax: +``{{`` and ``}}``. + +Still, if you want to use AngularJS and Twig in the same template, there are +two ways to make it work depending on the amount of AngularJS you need to +include in your templates: + +* Escaping the AngularJS delimiters by wrapping AngularJS sections with the + ``{% verbatim %}`` tag or by escaping each delimiter via ``{{ '{{' }}`` and + ``{{ '}}' }}``; + +* Changing the delimiters of one of the template engines (depending on which + engine you introduced last): + + * For AngularJS, change the interpolation tags using the + ``interpolateProvider`` service, for instance at the module initialization + time: + + .. code-block:: javascript + + angular.module('myApp', []).config(function($interpolateProvider) { + $interpolateProvider.startSymbol('{[').endSymbol(']}'); + }); + + * For Twig, change the delimiters via the ``tag_variable`` Lexer option: + + .. code-block:: php + + $env->setLexer(new Twig_Lexer($env, array( + 'tag_variable' => array('{[', ']}'), + ))); + .. _callback: http://www.php.net/manual/en/function.is-callable.php diff --git a/lib/silex/vendor/twig/twig/doc/tags/autoescape.rst b/lib/silex/vendor/twig/twig/doc/tags/autoescape.rst index 4208d1a31..8aa8c75d1 100644 --- a/lib/silex/vendor/twig/twig/doc/tags/autoescape.rst +++ b/lib/silex/vendor/twig/twig/doc/tags/autoescape.rst @@ -6,8 +6,6 @@ template to be escaped or not by using the ``autoescape`` tag: .. code-block:: jinja - {# The following syntax works as of Twig 1.8 -- see the note below for previous versions #} - {% autoescape %} Everything will be automatically escaped in this block using the HTML strategy diff --git a/lib/silex/vendor/twig/twig/doc/tags/embed.rst b/lib/silex/vendor/twig/twig/doc/tags/embed.rst index 5a6a0299a..66fc21b5f 100644 --- a/lib/silex/vendor/twig/twig/doc/tags/embed.rst +++ b/lib/silex/vendor/twig/twig/doc/tags/embed.rst @@ -170,9 +170,9 @@ The ``embed`` tag takes the exact same arguments as the ``include`` tag: .. warning:: As embedded templates do not have "names", auto-escaping strategies based - on the template "filename" won't work as expected if you change the - context (for instance, if you embed a CSS/JavaScript template into an HTML - one). In that case, explicitly set the default auto-escaping strategy with - the ``autoescape`` tag. + on the template name won't work as expected if you change the context (for + instance, if you embed a CSS/JavaScript template into an HTML one). In that + case, explicitly set the default auto-escaping strategy with the + ``autoescape`` tag. .. seealso:: :doc:`include<../tags/include>` diff --git a/lib/silex/vendor/twig/twig/doc/tags/extends.rst b/lib/silex/vendor/twig/twig/doc/tags/extends.rst index 1ad2b12b1..8bf29c4f9 100644 --- a/lib/silex/vendor/twig/twig/doc/tags/extends.rst +++ b/lib/silex/vendor/twig/twig/doc/tags/extends.rst @@ -131,8 +131,8 @@ to variables from outer scopes: Block Shortcuts --------------- -For blocks with few content, it's possible to use a shortcut syntax. The -following constructs do the same: +For blocks with little content, it's possible to use a shortcut syntax. The +following constructs do the same thing: .. code-block:: jinja @@ -153,13 +153,17 @@ Twig supports dynamic inheritance by using a variable as the base template: {% extends some_var %} -If the variable evaluates to a ``Twig_Template`` object, Twig will use it as -the parent template:: +If the variable evaluates to a ``Twig_Template`` or a ``Twig_TemplateWrapper`` +instance, Twig will use it as the parent template:: // {% extends layout %} + // deprecated as of Twig 1.28 $layout = $twig->loadTemplate('some_layout_template.twig'); + // as of Twig 1.28 + $layout = $twig->load('some_layout_template.twig'); + $twig->display('template.twig', array('layout' => $layout)); .. versionadded:: 1.2 diff --git a/lib/silex/vendor/twig/twig/doc/tags/include.rst b/lib/silex/vendor/twig/twig/doc/tags/include.rst index da18dc65e..24ff24db6 100644 --- a/lib/silex/vendor/twig/twig/doc/tags/include.rst +++ b/lib/silex/vendor/twig/twig/doc/tags/include.rst @@ -50,14 +50,18 @@ The template name can be any valid Twig expression: {% include some_var %} {% include ajax ? 'ajax.html' : 'not_ajax.html' %} -And if the expression evaluates to a ``Twig_Template`` object, Twig will use it -directly:: +And if the expression evaluates to a ``Twig_Template`` or a +``Twig_TemplateWrapper`` instance, Twig will use it directly:: // {% include template %} + // deprecated as of Twig 1.28 $template = $twig->loadTemplate('some_template.twig'); - $twig->loadTemplate('template.twig')->display(array('template' => $template)); + // as of Twig 1.28 + $template = $twig->load('some_template.twig'); + + $twig->display('template.twig', array('template' => $template)); .. versionadded:: 1.2 The ``ignore missing`` feature has been added in Twig 1.2. diff --git a/lib/silex/vendor/twig/twig/doc/tags/index.rst b/lib/silex/vendor/twig/twig/doc/tags/index.rst index e6a632b7d..dbe2459e3 100644 --- a/lib/silex/vendor/twig/twig/doc/tags/index.rst +++ b/lib/silex/vendor/twig/twig/doc/tags/index.rst @@ -22,3 +22,4 @@ Tags spaceless use verbatim + with diff --git a/lib/silex/vendor/twig/twig/doc/tags/macro.rst b/lib/silex/vendor/twig/twig/doc/tags/macro.rst index 60a1567d1..a8aa76fd9 100644 --- a/lib/silex/vendor/twig/twig/doc/tags/macro.rst +++ b/lib/silex/vendor/twig/twig/doc/tags/macro.rst @@ -13,7 +13,7 @@ Here is a small example of a macro that renders a form element: {% endmacro %} -Macros differs from native PHP functions in a few ways: +Macros differ from native PHP functions in a few ways: * Default argument values are defined by using the ``default`` filter in the macro body; @@ -31,6 +31,9 @@ variables. You can pass the whole context as an argument by using the special ``_context`` variable. +Import +------ + Macros can be defined in any template, and need to be "imported" before being used (see the documentation for the :doc:`import<../tags/import>` tag for more information): @@ -83,4 +86,18 @@ import it locally:
{% endmacro %} +Named Macro End-Tags +-------------------- + +Twig allows you to put the name of the macro after the end tag for better +readability: + +.. code-block:: jinja + + {% macro input() %} + ... + {% endmacro input %} + +Of course, the name after the ``endmacro`` word must match the macro name. + .. seealso:: :doc:`from<../tags/from>`, :doc:`import<../tags/import>` diff --git a/lib/silex/vendor/twig/twig/doc/tags/use.rst b/lib/silex/vendor/twig/twig/doc/tags/use.rst index 071b1975e..a6fdefb46 100644 --- a/lib/silex/vendor/twig/twig/doc/tags/use.rst +++ b/lib/silex/vendor/twig/twig/doc/tags/use.rst @@ -10,7 +10,7 @@ regular templates. It is mainly used by projects that need to make template blocks reusable without using inheritance. -Template inheritance is one of the most powerful Twig's feature but it is +Template inheritance is one of the most powerful features of Twig but it is limited to single inheritance; a template can only extend one other template. This limitation makes template inheritance simple to understand and easy to debug: diff --git a/lib/silex/vendor/twig/twig/doc/tags/with.rst b/lib/silex/vendor/twig/twig/doc/tags/with.rst new file mode 100644 index 000000000..815b06992 --- /dev/null +++ b/lib/silex/vendor/twig/twig/doc/tags/with.rst @@ -0,0 +1,44 @@ +``with`` +======== + +.. versionadded:: 1.28 + The ``with`` tag was added in Twig 1.28. + +Use the ``with`` tag to create a new inner scope. Variables set within this +scope are not visible outside of the scope: + +.. code-block:: jinja + + {% with %} + {% set foo = 42 %} + {{ foo }} foo is 42 here + {% endwith %} + foo is not visible here any longer + +Instead of defining variables at the beginning of the scope, you can pass a +hash of variables you want to define in the ``with`` tag; the previous example +is equivalent to the following one: + +.. code-block:: jinja + + {% with { foo: 42 } %} + {{ foo }} foo is 42 here + {% endwith %} + foo is not visible here any longer + + {# it works with any expression that resolves to a hash #} + {% set vars = { foo: 42 } %} + {% with vars %} + ... + {% endwith %} + +By default, the inner scope has access to the outer scope context; you can +disable this behavior by appending the ``only`` keyword: + +.. code-block:: jinja + + {% set bar = 'bar' %} + {% with { foo: 42 } only %} + {# only foo is defined #} + {# bar is not defined #} + {% endwith %} diff --git a/lib/silex/vendor/twig/twig/doc/templates.rst b/lib/silex/vendor/twig/twig/doc/templates.rst index 2d59b1a12..3d60779a0 100644 --- a/lib/silex/vendor/twig/twig/doc/templates.rst +++ b/lib/silex/vendor/twig/twig/doc/templates.rst @@ -59,6 +59,7 @@ Many IDEs support syntax highlighting and auto-completion for Twig: * *Notepad++* via the `Notepad++ Twig Highlighter`_ * *Emacs* via `web-mode.el`_ * *Atom* via the `PHP-twig for atom`_ +* *Visual Studio Code* via the `Twig pack`_ Also, `TwigFiddle`_ is an online service that allows you to execute Twig templates from a browser; it supports all versions of Twig. @@ -127,7 +128,7 @@ Global Variables The following variables are always available in templates: -* ``_self``: references the current template (deprecated since Twig 1.20); +* ``_self``: references the current template; * ``_context``: references the current context; * ``_charset``: references the current charset. @@ -293,25 +294,24 @@ designers or yourself: Including other Templates ------------------------- -The :doc:`include` tag is useful to include a template and -return the rendered content of that template into the current one: +The :doc:`include` function is useful to include a template +and return the rendered content of that template into the current one: .. code-block:: jinja - {% include 'sidebar.html' %} + {{ include('sidebar.html') }} -Per default included templates are passed the current context. - -The context that is passed to the included template includes variables defined -in the template: +By default, included templates have access to the same context as the template +which includes them. This means that any variable defined in the main template +will be available in the included template too: .. code-block:: jinja {% for box in boxes %} - {% include "render_box.html" %} + {{ include('render_box.html') }} {% endfor %} -The included template ``render_box.html`` is able to access ``box``. +The included template ``render_box.html`` is able to access the ``box`` variable. The filename of the template depends on the template loader. For instance, the ``Twig_Loader_Filesystem`` allows you to access other templates by giving the @@ -319,7 +319,7 @@ filename. You can access templates in subdirectories with a slash: .. code-block:: jinja - {% include "sections/articles/sidebar.html" %} + {{ include('sections/articles/sidebar.html') }} This behavior depends on the application embedding Twig. @@ -424,10 +424,8 @@ everything by default. Twig supports both, automatic escaping is enabled by default. -.. note:: - - Automatic escaping is only supported if the *escaper* extension has been - enabled (which is the default). +The automatic escaping strategy can be configured via the +:ref:`autoescape` option and defaults to ``html``. Working with Manual Escaping ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -587,7 +585,9 @@ exist: string. They are useful whenever you need a string in the template (for example as arguments to function calls, filters or just to extend or include a template). A string can contain a delimiter if it is preceded by a - backslash (``\``) -- like in ``'It\'s good'``. + backslash (``\``) -- like in ``'It\'s good'``. If the string contains a + backslash (e.g. ``'c:\Program Files'``) escape it by doubling it + (e.g. ``'c:\\Program Files'``). * ``42`` / ``42.23``: Integers and floating point numbers are created by just writing the number down. If a dot is present the number is a float, @@ -611,7 +611,8 @@ exist: { 2: 'foo', 4: 'bar' } {# keys as expressions (the expression must be enclosed into parentheses) -- as of Twig 1.5 #} - { (1 + 1): 'foo', (a ~ 'b'): 'bar' } + {% set foo = 'foo' %} + { (foo): 'foo', (1 + 1): 'bar', (foo ~ 'b'): 'baz' } * ``true`` / ``false``: ``true`` represents the true value, ``false`` represents the false value. @@ -771,15 +772,27 @@ Other Operators .. versionadded:: 1.12.0 Support for the extended ternary operator was added in Twig 1.12.0. -The following operators are very useful but don't fit into any of the other -categories: - -* ``..``: Creates a sequence based on the operand before and after the - operator (this is just syntactic sugar for the :doc:`range` - function). +The following operators don't fit into any of the other categories: * ``|``: Applies a filter. +* ``..``: Creates a sequence based on the operand before and after the operator + (this is just syntactic sugar for the :doc:`range` function): + + .. code-block:: jinja + + {{ 1..5 }} + + {# equivalent to #} + {{ range(1, 5) }} + + Note that you must use parentheses when combining it with the filter operator + due to the :ref:`operator precedence rules `: + + .. code-block:: jinja + + (1..5)|join(', ') + * ``~``: Converts all operands into strings and concatenates them. ``{{ "Hello " ~ name ~ "!" }}`` would return (assuming ``name`` is ``'John'``) ``Hello John!``. @@ -796,13 +809,20 @@ categories: {{ foo ?: 'no' }} is the same as {{ foo ? foo : 'no' }} {{ foo ? 'yes' }} is the same as {{ foo ? 'yes' : '' }} +* ``??``: The null-coalescing operator: + + .. code-block:: jinja + + {# returns the value of foo if it is defined and not null, 'no' otherwise #} + {{ foo ?? 'no' }} + String Interpolation ~~~~~~~~~~~~~~~~~~~~ .. versionadded:: 1.5 String interpolation was added in Twig 1.5. -String interpolation (`#{expression}`) allows any valid expression to appear +String interpolation (``#{expression}``) allows any valid expression to appear within a *double-quoted string*. The result of evaluating that expression is inserted into the string: @@ -850,7 +870,7 @@ leading and or trailing whitespace: {# output 'no spaces' #} The above sample shows the default whitespace control modifier, and how you can -use it to remove whitespace around tags. Trimming space will consume all whitespace +use it to remove whitespace around tags. Trimming space will consume all whitespace for that side of the tag. It is possible to use whitespace trimming on one side of a tag: @@ -874,7 +894,7 @@ Extension` chapter. .. _`Twig bundle`: https://github.com/Anomareh/PHP-Twig.tmbundle .. _`Jinja syntax plugin`: http://jinja.pocoo.org/docs/integration/#vim -.. _`vim-twig plugin`: https://github.com/evidens/vim-twig +.. _`vim-twig plugin`: https://github.com/lumiliet/vim-twig .. _`Twig syntax plugin`: http://plugins.netbeans.org/plugin/37069/php-twig .. _`Twig plugin`: https://github.com/pulse00/Twig-Eclipse-Plugin .. _`Twig language definition`: https://github.com/gabrielcorpse/gedit-twig-template-language @@ -886,3 +906,4 @@ Extension` chapter. .. _`regular expressions`: http://php.net/manual/en/pcre.pattern.php .. _`PHP-twig for atom`: https://github.com/reesef/php-twig .. _`TwigFiddle`: http://twigfiddle.com/ +.. _`Twig pack`: https://marketplace.visualstudio.com/items?itemName=bajdzis.vscode-twig-pack diff --git a/lib/silex/vendor/twig/twig/doc/tests/empty.rst b/lib/silex/vendor/twig/twig/doc/tests/empty.rst index e5b55999d..639cdcc39 100644 --- a/lib/silex/vendor/twig/twig/doc/tests/empty.rst +++ b/lib/silex/vendor/twig/twig/doc/tests/empty.rst @@ -1,11 +1,22 @@ ``empty`` ========= -``empty`` checks if a variable is empty: +.. versionadded:: 1.33 + + Support for the ``__toString()`` magic method has been added in Twig 1.33. + +``empty`` checks if a variable is an empty string, an empty array, an empty +hash, exactly ``false``, or exactly ``null``. + +For objects that implement the ``Countable`` interface, ``empty`` will check the +return value of the ``count()`` method. + +For objects that implement the ``__toString()`` magic method (and not ``Countable``), +it will check if an empty string is returned. .. code-block:: jinja - {# evaluates to true if the foo variable is null, false, an empty array, or the empty string #} {% if foo is empty %} ... {% endif %} + diff --git a/lib/silex/vendor/twig/twig/ext/twig/php_twig.h b/lib/silex/vendor/twig/twig/ext/twig/php_twig.h index 27be05bb7..7fc1601e8 100644 --- a/lib/silex/vendor/twig/twig/ext/twig/php_twig.h +++ b/lib/silex/vendor/twig/twig/ext/twig/php_twig.h @@ -15,7 +15,7 @@ #ifndef PHP_TWIG_H #define PHP_TWIG_H -#define PHP_TWIG_VERSION "1.21.2" +#define PHP_TWIG_VERSION "1.35.0" #include "php.h" diff --git a/lib/silex/vendor/twig/twig/ext/twig/twig.c b/lib/silex/vendor/twig/twig/ext/twig/twig.c index 92d1addc7..6173c0067 100644 --- a/lib/silex/vendor/twig/twig/ext/twig/twig.c +++ b/lib/silex/vendor/twig/twig/ext/twig/twig.c @@ -31,6 +31,10 @@ #define Z_ADDREF_P(pz) (pz)->refcount++ #endif +#ifndef E_USER_DEPRECATED +#define E_USER_DEPRECATED (1<<14L) +#endif + #define FREE_DTOR(z) \ zval_dtor(z); \ efree(z); @@ -144,7 +148,7 @@ static zval *TWIG_GET_ARRAYOBJECT_ELEMENT(zval *object, zval *offset TSRMLS_DC) if (!retval) { if (!EG(exception)) { - zend_error(E_ERROR, "Undefined offset for object of type %s used as array", ce->name); + zend_error(E_ERROR, "Undefined offset for object of type %s used as array.", ce->name); } return NULL; } @@ -167,7 +171,7 @@ static int TWIG_ISSET_ARRAYOBJECT_ELEMENT(zval *object, zval *offset TSRMLS_DC) if (!retval) { if (!EG(exception)) { - zend_error(E_ERROR, "Undefined offset for object of type %s used as array", ce->name); + zend_error(E_ERROR, "Undefined offset for object of type %s used as array.", ce->name); } return 0; } @@ -811,15 +815,15 @@ PHP_FUNCTION(twig_template_get_attributes) } */ if (TWIG_INSTANCE_OF(object, zend_ce_arrayaccess TSRMLS_CC)) { - TWIG_RUNTIME_ERROR(template TSRMLS_CC, "Key \"%s\" in object with ArrayAccess of class \"%s\" does not exist", item, TWIG_GET_CLASS_NAME(object TSRMLS_CC)); + TWIG_RUNTIME_ERROR(template TSRMLS_CC, "Key \"%s\" in object with ArrayAccess of class \"%s\" does not exist.", item, TWIG_GET_CLASS_NAME(object TSRMLS_CC)); } else if (Z_TYPE_P(object) == IS_OBJECT) { - TWIG_RUNTIME_ERROR(template TSRMLS_CC, "Impossible to access a key \"%s\" on an object of class \"%s\" that does not implement ArrayAccess interface", item, TWIG_GET_CLASS_NAME(object TSRMLS_CC)); + TWIG_RUNTIME_ERROR(template TSRMLS_CC, "Impossible to access a key \"%s\" on an object of class \"%s\" that does not implement ArrayAccess interface.", item, TWIG_GET_CLASS_NAME(object TSRMLS_CC)); } else if (Z_TYPE_P(object) == IS_ARRAY) { if (0 == zend_hash_num_elements(Z_ARRVAL_P(object))) { - TWIG_RUNTIME_ERROR(template TSRMLS_CC, "Key \"%s\" does not exist as the array is empty", item); + TWIG_RUNTIME_ERROR(template TSRMLS_CC, "Key \"%s\" does not exist as the array is empty.", item); } else { char *array_keys = TWIG_IMPLODE_ARRAY_KEYS(", ", object TSRMLS_CC); - TWIG_RUNTIME_ERROR(template TSRMLS_CC, "Key \"%s\" for array with keys \"%s\" does not exist", item, array_keys); + TWIG_RUNTIME_ERROR(template TSRMLS_CC, "Key \"%s\" for array with keys \"%s\" does not exist.", item, array_keys); efree(array_keys); } } else { @@ -829,15 +833,15 @@ PHP_FUNCTION(twig_template_get_attributes) convert_to_string(object); TWIG_RUNTIME_ERROR(template TSRMLS_CC, (strcmp("array", type) == 0) - ? "Impossible to access a key (\"%s\") on a %s variable" - : "Impossible to access an attribute (\"%s\") on a %s variable", + ? "Impossible to access a key (\"%s\") on a %s variable." + : "Impossible to access an attribute (\"%s\") on a %s variable.", item, type_name); } else { convert_to_string(object); TWIG_RUNTIME_ERROR(template TSRMLS_CC, (strcmp("array", type) == 0) - ? "Impossible to access a key (\"%s\") on a %s variable (\"%s\")" - : "Impossible to access an attribute (\"%s\") on a %s variable (\"%s\")", + ? "Impossible to access a key (\"%s\") on a %s variable (\"%s\")." + : "Impossible to access an attribute (\"%s\") on a %s variable (\"%s\").", item, type_name, Z_STRVAL_P(object)); } zval_ptr_dtor(&object); @@ -883,11 +887,11 @@ PHP_FUNCTION(twig_template_get_attributes) if (Z_TYPE_P(object) == IS_NULL) { convert_to_string_ex(&object); - TWIG_RUNTIME_ERROR(template TSRMLS_CC, "Impossible to invoke a method (\"%s\") on a %s variable", item, type_name); + TWIG_RUNTIME_ERROR(template TSRMLS_CC, "Impossible to invoke a method (\"%s\") on a %s variable.", item, type_name); } else { convert_to_string_ex(&object); - TWIG_RUNTIME_ERROR(template TSRMLS_CC, "Impossible to invoke a method (\"%s\") on a %s variable (\"%s\")", item, type_name, Z_STRVAL_P(object)); + TWIG_RUNTIME_ERROR(template TSRMLS_CC, "Impossible to invoke a method (\"%s\") on a %s variable (\"%s\").", item, type_name, Z_STRVAL_P(object)); } zval_ptr_dtor(&object); @@ -916,8 +920,8 @@ PHP_FUNCTION(twig_template_get_attributes) return true; } - if ($this->env->hasExtension('sandbox')) { - $this->env->getExtension('sandbox')->checkPropertyAllowed($object, $item); + if ($this->env->hasExtension('Twig_Extension_Sandbox')) { + $this->env->getExtension('Twig_Extension_Sandbox')->checkPropertyAllowed($object, $item); } return $object->$item; @@ -935,8 +939,8 @@ PHP_FUNCTION(twig_template_get_attributes) efree(item); RETURN_TRUE; } - if (TWIG_CALL_SB(TWIG_PROPERTY_CHAR(template, "env" TSRMLS_CC), "hasExtension", "sandbox" TSRMLS_CC)) { - TWIG_CALL_ZZ(TWIG_CALL_S(TWIG_PROPERTY_CHAR(template, "env" TSRMLS_CC), "getExtension", "sandbox" TSRMLS_CC), "checkPropertyAllowed", object, zitem TSRMLS_CC); + if (TWIG_CALL_SB(TWIG_PROPERTY_CHAR(template, "env" TSRMLS_CC), "hasExtension", "Twig_Extension_Sandbox" TSRMLS_CC)) { + TWIG_CALL_ZZ(TWIG_CALL_S(TWIG_PROPERTY_CHAR(template, "env" TSRMLS_CC), "getExtension", "Twig_Extension_Sandbox" TSRMLS_CC), "checkPropertyAllowed", object, zitem TSRMLS_CC); } if (EG(exception)) { efree(item); @@ -987,6 +991,7 @@ PHP_FUNCTION(twig_template_get_attributes) char *lcItem = TWIG_STRTOLOWER(item, item_len); int lcItem_length; char *method = NULL; + char *methodForDeprecation = NULL; char *tmp_method_name_get; char *tmp_method_name_is; zval *zmethod; @@ -1000,6 +1005,8 @@ PHP_FUNCTION(twig_template_get_attributes) sprintf(tmp_method_name_is, "is%s", lcItem); tmp_methods = TWIG_GET_ARRAY_ELEMENT(tmp_class, "methods", strlen("methods") TSRMLS_CC); + methodForDeprecation = emalloc(item_len + 1); + sprintf(methodForDeprecation, "%s", item); if (TWIG_GET_ARRAY_ELEMENT(tmp_methods, lcItem, lcItem_length TSRMLS_CC)) { method = item; @@ -1020,7 +1027,7 @@ PHP_FUNCTION(twig_template_get_attributes) return null; } - throw new Twig_Error_Runtime(sprintf('Method "%s" for object "%s" does not exist', $item, get_class($object)), -1, $this->getTemplateName()); + throw new Twig_Error_Runtime(sprintf('Method "%s" for object "%s" does not exist.', $item, get_class($object)), -1, $this->getTemplateName()); } if ($isDefinedTest) { @@ -1040,7 +1047,7 @@ PHP_FUNCTION(twig_template_get_attributes) efree(item); return; } - TWIG_RUNTIME_ERROR(template TSRMLS_CC, "Method \"%s\" for object \"%s\" does not exist", item, TWIG_GET_CLASS_NAME(object TSRMLS_CC)); + TWIG_RUNTIME_ERROR(template TSRMLS_CC, "Neither the property \"%s\" nor one of the methods \"%s()\", \"get%s()\"/\"is%s()\" or \"__call()\" exist and have public access in class \"%s\".", item, item, item, item, TWIG_GET_CLASS_NAME(object TSRMLS_CC)); efree(item); return; } @@ -1052,14 +1059,14 @@ PHP_FUNCTION(twig_template_get_attributes) RETURN_TRUE; } /* - if ($this->env->hasExtension('sandbox')) { - $this->env->getExtension('sandbox')->checkMethodAllowed($object, $method); + if ($this->env->hasExtension('Twig_Extension_Sandbox')) { + $this->env->getExtension('Twig_Extension_Sandbox')->checkMethodAllowed($object, $method); } */ MAKE_STD_ZVAL(zmethod); ZVAL_STRING(zmethod, method, 1); - if (TWIG_CALL_SB(TWIG_PROPERTY_CHAR(template, "env" TSRMLS_CC), "hasExtension", "sandbox" TSRMLS_CC)) { - TWIG_CALL_ZZ(TWIG_CALL_S(TWIG_PROPERTY_CHAR(template, "env" TSRMLS_CC), "getExtension", "sandbox" TSRMLS_CC), "checkMethodAllowed", object, zmethod TSRMLS_CC); + if (TWIG_CALL_SB(TWIG_PROPERTY_CHAR(template, "env" TSRMLS_CC), "hasExtension", "Twig_Extension_Sandbox" TSRMLS_CC)) { + TWIG_CALL_ZZ(TWIG_CALL_S(TWIG_PROPERTY_CHAR(template, "env" TSRMLS_CC), "getExtension", "Twig_Extension_Sandbox" TSRMLS_CC), "checkMethodAllowed", object, zmethod TSRMLS_CC); } zval_ptr_dtor(&zmethod); if (EG(exception)) { @@ -1094,34 +1101,115 @@ PHP_FUNCTION(twig_template_get_attributes) efree(tmp_method_name_get); efree(tmp_method_name_is); efree(lcItem); - } /* - // useful when calling a template method from a template - // this is not supported but unfortunately heavily used in the Symfony profiler + // @deprecated in 1.28 if ($object instanceof Twig_TemplateInterface) { + $self = $object->getTemplateName() === $this->getTemplateName(); + $message = sprintf('Calling "%s" on template "%s" from template "%s" is deprecated since version 1.28 and won\'t be supported anymore in 2.0.', $item, $object->getTemplateName(), $this->getTemplateName()); + if ('renderBlock' === $method || 'displayBlock' === $method) { + $message .= sprintf(' Use block("%s"%s) instead).', $arguments[0], $self ? '' : ', template'); + } elseif ('hasBlock' === $method) { + $message .= sprintf(' Use "block("%s"%s) is defined" instead).', $arguments[0], $self ? '' : ', template'); + } elseif ('render' === $method || 'display' === $method) { + $message .= sprintf(' Use include("%s") instead).', $object->getTemplateName()); + } + @trigger_error($message, E_USER_DEPRECATED); + return $ret === '' ? '' : new Twig_Markup($ret, $this->env->getCharset()); } return $ret; */ - efree(item); - // ret can be null, if e.g. the called method throws an exception - if (ret) { - if (TWIG_INSTANCE_OF_USERLAND(object, "Twig_TemplateInterface" TSRMLS_CC)) { - if (Z_STRLEN_P(ret) != 0) { - zval *charset = TWIG_CALL_USER_FUNC_ARRAY(TWIG_PROPERTY_CHAR(template, "env" TSRMLS_CC), "getCharset", NULL TSRMLS_CC); - TWIG_NEW(return_value, "Twig_Markup", ret, charset TSRMLS_CC); - zval_ptr_dtor(&charset); - if (ret) { - zval_ptr_dtor(&ret); + efree(item); + // ret can be null, if e.g. the called method throws an exception + if (ret) { + if (TWIG_INSTANCE_OF_USERLAND(object, "Twig_TemplateInterface" TSRMLS_CC)) { + int self; + int old_error_reporting; + zval *object_filename; + zval *this_filename; + zval *filename_func; + char *deprecation_message_complement = NULL; + char *deprecation_message = NULL; + + MAKE_STD_ZVAL(object_filename); + MAKE_STD_ZVAL(this_filename); + MAKE_STD_ZVAL(filename_func); + + // Get templates names + ZVAL_STRINGL(filename_func, "getTemplateName", sizeof("getTemplateName")-1, 1); + call_user_function(EG(function_table), &object, filename_func, object_filename, 0, 0 TSRMLS_CC); + ZVAL_STRINGL(filename_func, "getTemplateName", sizeof("getTemplateName")-1, 1); + call_user_function(EG(function_table), &template, filename_func, this_filename, 0, 0 TSRMLS_CC); + + self = (strcmp(Z_STRVAL_P(object_filename), Z_STRVAL_P(this_filename)) == 0); + + if (strcmp(methodForDeprecation, "renderBlock") == 0 || strcmp(methodForDeprecation, "displayBlock") == 0) { + zval **arg0; + zend_hash_index_find(HASH_OF(arguments), 0, (void **) &arg0); + asprintf( + &deprecation_message_complement, + " Use block(\"%s\"%s) instead).", + Z_STRVAL_PP(arg0), + self ? "" : ", template" + ); + } else if (strcmp(methodForDeprecation, "hasBlock") == 0) { + zval **arg0; + zend_hash_index_find(HASH_OF(arguments), 0, (void **) &arg0); + asprintf( + &deprecation_message_complement, + " Use \"block(\"%s\"%s) is defined\" instead).", + Z_STRVAL_PP(arg0), + self ? "" : ", template" + ); + } else if (strcmp(methodForDeprecation, "render") == 0 || strcmp(methodForDeprecation, "display") == 0) { + asprintf( + &deprecation_message_complement, + " Use include(\"%s\") instead).", + Z_STRVAL_P(object_filename) + ); + } else { + deprecation_message_complement = (char*)calloc(0, sizeof(char)); } - return; + + asprintf( + &deprecation_message, + "Calling \"%s\" on template \"%s\" from template \"%s\" is deprecated since version 1.28 and won't be supported anymore in 2.0.%s", + methodForDeprecation, + Z_STRVAL_P(object_filename), + Z_STRVAL_P(this_filename), + deprecation_message_complement + ); + + old_error_reporting = EG(error_reporting); + EG(error_reporting) = 0; + zend_error(E_USER_DEPRECATED, "%s", deprecation_message); + EG(error_reporting) = old_error_reporting; + + FREE_DTOR(filename_func) + FREE_DTOR(object_filename) + FREE_DTOR(this_filename) + free(deprecation_message); + free(deprecation_message_complement); + + if (Z_STRLEN_P(ret) != 0) { + zval *charset = TWIG_CALL_USER_FUNC_ARRAY(TWIG_PROPERTY_CHAR(template, "env" TSRMLS_CC), "getCharset", NULL TSRMLS_CC); + TWIG_NEW(return_value, "Twig_Markup", ret, charset TSRMLS_CC); + zval_ptr_dtor(&charset); + if (ret) { + zval_ptr_dtor(&ret); + } + efree(methodForDeprecation); + return; + } + } + + RETVAL_ZVAL(ret, 1, 0); + if (free_ret) { + zval_ptr_dtor(&ret); } } - RETVAL_ZVAL(ret, 1, 0); - if (free_ret) { - zval_ptr_dtor(&ret); - } + efree(methodForDeprecation); } } diff --git a/lib/silex/vendor/twig/twig/lib/Twig/Autoloader.php b/lib/silex/vendor/twig/twig/lib/Twig/Autoloader.php index d47583fbf..212af5445 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/Autoloader.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/Autoloader.php @@ -3,31 +3,31 @@ /* * This file is part of Twig. * - * (c) 2009 Fabien Potencier + * (c) Fabien Potencier * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -@trigger_error('The Twig_Autoloader class is deprecated and will be removed in 2.0. Use Composer instead.', E_USER_DEPRECATED); +@trigger_error('The Twig_Autoloader class is deprecated since version 1.21 and will be removed in 2.0. Use Composer instead.', E_USER_DEPRECATED); /** * Autoloads Twig classes. * * @author Fabien Potencier * - * @deprecated Use Composer instead. Will be removed in Twig 2.0. + * @deprecated since 1.21 and will be removed in 2.0. Use Composer instead. 2.0. */ class Twig_Autoloader { /** * Registers Twig_Autoloader as an SPL autoloader. * - * @param bool $prepend Whether to prepend the autoloader or not. + * @param bool $prepend whether to prepend the autoloader or not */ public static function register($prepend = false) { - @trigger_error('Using Twig_Autoloader is deprecated. Use Composer instead.', E_USER_DEPRECATED); + @trigger_error('Using Twig_Autoloader is deprecated since version 1.21. Use Composer instead.', E_USER_DEPRECATED); if (PHP_VERSION_ID < 50300) { spl_autoload_register(array(__CLASS__, 'autoload')); @@ -39,7 +39,7 @@ class Twig_Autoloader /** * Handles autoloading of classes. * - * @param string $class A class name. + * @param string $class a class name */ public static function autoload($class) { diff --git a/lib/silex/vendor/twig/twig/lib/Twig/BaseNodeVisitor.php b/lib/silex/vendor/twig/twig/lib/Twig/BaseNodeVisitor.php index 3c6ef6626..d8ef02fb2 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/BaseNodeVisitor.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/BaseNodeVisitor.php @@ -16,9 +16,6 @@ */ abstract class Twig_BaseNodeVisitor implements Twig_NodeVisitorInterface { - /** - * {@inheritdoc} - */ final public function enterNode(Twig_NodeInterface $node, Twig_Environment $env) { if (!$node instanceof Twig_Node) { @@ -28,9 +25,6 @@ abstract class Twig_BaseNodeVisitor implements Twig_NodeVisitorInterface return $this->doEnterNode($node, $env); } - /** - * {@inheritdoc} - */ final public function leaveNode(Twig_NodeInterface $node, Twig_Environment $env) { if (!$node instanceof Twig_Node) { @@ -43,9 +37,6 @@ abstract class Twig_BaseNodeVisitor implements Twig_NodeVisitorInterface /** * Called before child nodes are visited. * - * @param Twig_Node $node The node to visit - * @param Twig_Environment $env The Twig environment instance - * * @return Twig_Node The modified node */ abstract protected function doEnterNode(Twig_Node $node, Twig_Environment $env); @@ -53,10 +44,11 @@ abstract class Twig_BaseNodeVisitor implements Twig_NodeVisitorInterface /** * Called after child nodes are visited. * - * @param Twig_Node $node The node to visit - * @param Twig_Environment $env The Twig environment instance - * * @return Twig_Node|false The modified node or false if the node must be removed */ abstract protected function doLeaveNode(Twig_Node $node, Twig_Environment $env); } + +class_alias('Twig_BaseNodeVisitor', 'Twig\NodeVisitor\AbstractNodeVisitor', false); +class_exists('Twig_Environment'); +class_exists('Twig_Node'); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/Cache/Filesystem.php b/lib/silex/vendor/twig/twig/lib/Twig/Cache/Filesystem.php new file mode 100644 index 000000000..65976282b --- /dev/null +++ b/lib/silex/vendor/twig/twig/lib/Twig/Cache/Filesystem.php @@ -0,0 +1,93 @@ + + */ +class Twig_Cache_Filesystem implements Twig_CacheInterface +{ + const FORCE_BYTECODE_INVALIDATION = 1; + + private $directory; + private $options; + + /** + * @param $directory string The root cache directory + * @param $options int A set of options + */ + public function __construct($directory, $options = 0) + { + $this->directory = rtrim($directory, '\/').'/'; + $this->options = $options; + } + + public function generateKey($name, $className) + { + $hash = hash('sha256', $className); + + return $this->directory.$hash[0].$hash[1].'/'.$hash.'.php'; + } + + public function load($key) + { + if (file_exists($key)) { + @include_once $key; + } + } + + public function write($key, $content) + { + $dir = dirname($key); + if (!is_dir($dir)) { + if (false === @mkdir($dir, 0777, true)) { + if (PHP_VERSION_ID >= 50300) { + clearstatcache(true, $dir); + } + if (!is_dir($dir)) { + throw new RuntimeException(sprintf('Unable to create the cache directory (%s).', $dir)); + } + } + } elseif (!is_writable($dir)) { + throw new RuntimeException(sprintf('Unable to write in the cache directory (%s).', $dir)); + } + + $tmpFile = tempnam($dir, basename($key)); + if (false !== @file_put_contents($tmpFile, $content) && @rename($tmpFile, $key)) { + @chmod($key, 0666 & ~umask()); + + if (self::FORCE_BYTECODE_INVALIDATION == ($this->options & self::FORCE_BYTECODE_INVALIDATION)) { + // Compile cached file into bytecode cache + if (function_exists('opcache_invalidate')) { + opcache_invalidate($key, true); + } elseif (function_exists('apc_compile_file')) { + apc_compile_file($key); + } + } + + return; + } + + throw new RuntimeException(sprintf('Failed to write cache file "%s".', $key)); + } + + public function getTimestamp($key) + { + if (!file_exists($key)) { + return 0; + } + + return (int) @filemtime($key); + } +} + +class_alias('Twig_Cache_Filesystem', 'Twig\Cache\FilesystemCache', false); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/Cache/Null.php b/lib/silex/vendor/twig/twig/lib/Twig/Cache/Null.php new file mode 100644 index 000000000..69d1d2f98 --- /dev/null +++ b/lib/silex/vendor/twig/twig/lib/Twig/Cache/Null.php @@ -0,0 +1,40 @@ + + */ +class Twig_Cache_Null implements Twig_CacheInterface +{ + public function generateKey($name, $className) + { + return ''; + } + + public function write($key, $content) + { + } + + public function load($key) + { + } + + public function getTimestamp($key) + { + return 0; + } +} + +class_alias('Twig_Cache_Null', 'Twig\Cache\NullCache', false); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/CacheInterface.php b/lib/silex/vendor/twig/twig/lib/Twig/CacheInterface.php new file mode 100644 index 000000000..776808bfe --- /dev/null +++ b/lib/silex/vendor/twig/twig/lib/Twig/CacheInterface.php @@ -0,0 +1,58 @@ + + */ +interface Twig_CacheInterface +{ + /** + * Generates a cache key for the given template class name. + * + * @param string $name The template name + * @param string $className The template class name + * + * @return string + */ + public function generateKey($name, $className); + + /** + * Writes the compiled template to cache. + * + * @param string $key The cache key + * @param string $content The template representation as a PHP class + */ + public function write($key, $content); + + /** + * Loads a template from the cache. + * + * @param string $key The cache key + */ + public function load($key); + + /** + * Returns the modification timestamp of a key. + * + * @param string $key The cache key + * + * @return int + */ + public function getTimestamp($key); +} + +class_alias('Twig_CacheInterface', 'Twig\Cache\CacheInterface', false); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/Compiler.php b/lib/silex/vendor/twig/twig/lib/Twig/Compiler.php index abea3aafe..e90bc987d 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/Compiler.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/Compiler.php @@ -3,8 +3,8 @@ /* * This file is part of Twig. * - * (c) 2009 Fabien Potencier - * (c) 2009 Armin Ronacher + * (c) Fabien Potencier + * (c) Armin Ronacher * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -26,25 +26,25 @@ class Twig_Compiler implements Twig_CompilerInterface protected $sourceLine; protected $filename; - /** - * Constructor. - * - * @param Twig_Environment $env The twig environment instance - */ public function __construct(Twig_Environment $env) { $this->env = $env; } + /** + * @deprecated since 1.25 (to be removed in 2.0) + */ public function getFilename() { + @trigger_error(sprintf('The %s() method is deprecated since version 1.25 and will be removed in 2.0.', __FUNCTION__), E_USER_DEPRECATED); + return $this->filename; } /** * Returns the environment instance related to this compiler. * - * @return Twig_Environment The environment instance + * @return Twig_Environment */ public function getEnvironment() { @@ -67,7 +67,7 @@ class Twig_Compiler implements Twig_CompilerInterface * @param Twig_NodeInterface $node The node to compile * @param int $indentation The current indentation * - * @return Twig_Compiler The current compiler instance + * @return $this */ public function compile(Twig_NodeInterface $node, $indentation = 0) { @@ -80,7 +80,8 @@ class Twig_Compiler implements Twig_CompilerInterface $this->indentation = $indentation; if ($node instanceof Twig_Node_Module) { - $this->filename = $node->getAttribute('filename'); + // to be removed in 2.0 + $this->filename = $node->getTemplateName(); } $node->compile($this); @@ -91,7 +92,7 @@ class Twig_Compiler implements Twig_CompilerInterface public function subcompile(Twig_NodeInterface $node, $raw = true) { if (false === $raw) { - $this->addIndentation(); + $this->source .= str_repeat(' ', $this->indentation * 4); } $node->compile($this); @@ -104,7 +105,7 @@ class Twig_Compiler implements Twig_CompilerInterface * * @param string $string The string * - * @return Twig_Compiler The current compiler instance + * @return $this */ public function raw($string) { @@ -116,14 +117,13 @@ class Twig_Compiler implements Twig_CompilerInterface /** * Writes a string to the compiled code by adding indentation. * - * @return Twig_Compiler The current compiler instance + * @return $this */ public function write() { $strings = func_get_args(); foreach ($strings as $string) { - $this->addIndentation(); - $this->source .= $string; + $this->source .= str_repeat(' ', $this->indentation * 4).$string; } return $this; @@ -132,10 +132,14 @@ class Twig_Compiler implements Twig_CompilerInterface /** * Appends an indentation to the current PHP code after compilation. * - * @return Twig_Compiler The current compiler instance + * @return $this + * + * @deprecated since 1.27 (to be removed in 2.0). */ public function addIndentation() { + @trigger_error('The '.__METHOD__.' method is deprecated since version 1.27 and will be removed in 2.0. Use write(\'\') instead.', E_USER_DEPRECATED); + $this->source .= str_repeat(' ', $this->indentation * 4); return $this; @@ -146,7 +150,7 @@ class Twig_Compiler implements Twig_CompilerInterface * * @param string $value The string * - * @return Twig_Compiler The current compiler instance + * @return $this */ public function string($value) { @@ -160,12 +164,12 @@ class Twig_Compiler implements Twig_CompilerInterface * * @param mixed $value The value to convert * - * @return Twig_Compiler The current compiler instance + * @return $this */ public function repr($value) { if (is_int($value) || is_float($value)) { - if (false !== $locale = setlocale(LC_NUMERIC, 0)) { + if (false !== $locale = setlocale(LC_NUMERIC, '0')) { setlocale(LC_NUMERIC, 'C'); } @@ -201,28 +205,28 @@ class Twig_Compiler implements Twig_CompilerInterface /** * Adds debugging information. * - * @param Twig_NodeInterface $node The related twig node - * - * @return Twig_Compiler The current compiler instance + * @return $this */ public function addDebugInfo(Twig_NodeInterface $node) { - if ($node->getLine() != $this->lastLine) { - $this->write(sprintf("// line %d\n", $node->getLine())); + if ($node->getTemplateLine() != $this->lastLine) { + $this->write(sprintf("// line %d\n", $node->getTemplateLine())); // when mbstring.func_overload is set to 2 // mb_substr_count() replaces substr_count() // but they have different signatures! if (((int) ini_get('mbstring.func_overload')) & 2) { + @trigger_error('Support for having "mbstring.func_overload" different from 0 is deprecated version 1.29 and will be removed in 2.0.', E_USER_DEPRECATED); + // this is much slower than the "right" version $this->sourceLine += mb_substr_count(mb_substr($this->source, $this->sourceOffset), "\n"); } else { $this->sourceLine += substr_count($this->source, "\n", $this->sourceOffset); } $this->sourceOffset = strlen($this->source); - $this->debugInfo[$this->sourceLine] = $node->getLine(); + $this->debugInfo[$this->sourceLine] = $node->getTemplateLine(); - $this->lastLine = $node->getLine(); + $this->lastLine = $node->getTemplateLine(); } return $this; @@ -240,7 +244,7 @@ class Twig_Compiler implements Twig_CompilerInterface * * @param int $step The number of indentation to add * - * @return Twig_Compiler The current compiler instance + * @return $this */ public function indent($step = 1) { @@ -254,7 +258,7 @@ class Twig_Compiler implements Twig_CompilerInterface * * @param int $step The number of indentation to remove * - * @return Twig_Compiler The current compiler instance + * @return $this * * @throws LogicException When trying to outdent too much so the indentation would become negative */ @@ -262,7 +266,7 @@ class Twig_Compiler implements Twig_CompilerInterface { // can't outdent by more steps than the current indentation level if ($this->indentation < $step) { - throw new LogicException('Unable to call outdent() as the indentation would become negative'); + throw new LogicException('Unable to call outdent() as the indentation would become negative.'); } $this->indentation -= $step; @@ -275,3 +279,6 @@ class Twig_Compiler implements Twig_CompilerInterface return sprintf('__internal_%s', hash('sha256', uniqid(mt_rand(), true), false)); } } + +class_alias('Twig_Compiler', 'Twig\Compiler', false); +class_exists('Twig_Node'); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/CompilerInterface.php b/lib/silex/vendor/twig/twig/lib/Twig/CompilerInterface.php index 272c7672f..42872c9cd 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/CompilerInterface.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/CompilerInterface.php @@ -3,7 +3,7 @@ /* * This file is part of Twig. * - * (c) 2009 Fabien Potencier + * (c) Fabien Potencier * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -21,9 +21,7 @@ interface Twig_CompilerInterface /** * Compiles a node. * - * @param Twig_NodeInterface $node The node to compile - * - * @return Twig_CompilerInterface The current compiler instance + * @return $this */ public function compile(Twig_NodeInterface $node); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/ContainerRuntimeLoader.php b/lib/silex/vendor/twig/twig/lib/Twig/ContainerRuntimeLoader.php new file mode 100644 index 000000000..814ab58bc --- /dev/null +++ b/lib/silex/vendor/twig/twig/lib/Twig/ContainerRuntimeLoader.php @@ -0,0 +1,39 @@ + + * @author Robin Chalas + */ +class Twig_ContainerRuntimeLoader implements Twig_RuntimeLoaderInterface +{ + private $container; + + public function __construct(ContainerInterface $container) + { + $this->container = $container; + } + + public function load($class) + { + if ($this->container->has($class)) { + return $this->container->get($class); + } + } +} + +class_alias('Twig_ContainerRuntimeLoader', 'Twig\RuntimeLoader\ContainerRuntimeLoader', false); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/Environment.php b/lib/silex/vendor/twig/twig/lib/Twig/Environment.php index 6813a301d..5de2f27fd 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/Environment.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/Environment.php @@ -3,7 +3,7 @@ /* * This file is part of Twig. * - * (c) 2009 Fabien Potencier + * (c) Fabien Potencier * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -16,7 +16,12 @@ */ class Twig_Environment { - const VERSION = '1.21.2'; + const VERSION = '1.35.0'; + const VERSION_ID = 13500; + const MAJOR_VERSION = 1; + const MINOR_VERSION = 35; + const RELEASE_VERSION = 0; + const EXTRA_VERSION = ''; protected $charset; protected $loader; @@ -45,6 +50,16 @@ class Twig_Environment protected $filterCallbacks = array(); protected $staging; + private $originalCache; + private $bcWriteCacheFile = false; + private $bcGetCacheFilename = false; + private $lastModifiedExtension = 0; + private $extensionsByClass = array(); + private $runtimeLoaders = array(); + private $runtimes = array(); + private $optionsHash; + private $loading = array(); + /** * Constructor. * @@ -58,8 +73,9 @@ class Twig_Environment * * base_template_class: The base template class to use for generated * templates (default to Twig_Template). * - * * cache: An absolute path where to store the compiled templates, or - * false to disable compilation cache (default). + * * cache: An absolute path where to store the compiled templates, + * a Twig_Cache_Interface implementation, + * or false to disable compilation cache (default). * * * auto_reload: Whether to reload the template if the original source changed. * If you don't provide the auto_reload option, it will be @@ -72,14 +88,14 @@ class Twig_Environment * * false: disable auto-escaping * * true: equivalent to html * * html, js: set the autoescaping to one of the supported strategies - * * filename: set the autoescaping strategy based on the template filename extension - * * PHP callback: a PHP callback that returns an escaping strategy based on the template "filename" + * * name: set the autoescaping strategy based on the template name extension + * * PHP callback: a PHP callback that returns an escaping strategy based on the template "name" * * * optimizations: A flag that indicates which optimizations to apply * (default to -1 which means that all optimizations are enabled; * set it to 0 to disable). * - * @param Twig_LoaderInterface $loader A Twig_LoaderInterface instance + * @param Twig_LoaderInterface $loader * @param array $options An array of options */ public function __construct(Twig_LoaderInterface $loader = null, $options = array()) @@ -87,7 +103,7 @@ class Twig_Environment if (null !== $loader) { $this->setLoader($loader); } else { - @trigger_error('Not passing a Twig_LoaderInterface as the first constructor argument of Twig_Environment is deprecated.', E_USER_DEPRECATED); + @trigger_error('Not passing a Twig_LoaderInterface as the first constructor argument of Twig_Environment is deprecated since version 1.21.', E_USER_DEPRECATED); } $options = array_merge(array( @@ -112,6 +128,23 @@ class Twig_Environment $this->addExtension(new Twig_Extension_Escaper($options['autoescape'])); $this->addExtension(new Twig_Extension_Optimizer($options['optimizations'])); $this->staging = new Twig_Extension_Staging(); + + // For BC + if (is_string($this->originalCache)) { + $r = new ReflectionMethod($this, 'writeCacheFile'); + if ($r->getDeclaringClass()->getName() !== __CLASS__) { + @trigger_error('The Twig_Environment::writeCacheFile method is deprecated since version 1.22 and will be removed in Twig 2.0.', E_USER_DEPRECATED); + + $this->bcWriteCacheFile = true; + } + + $r = new ReflectionMethod($this, 'getCacheFilename'); + if ($r->getDeclaringClass()->getName() !== __CLASS__) { + @trigger_error('The Twig_Environment::getCacheFilename method is deprecated since version 1.22 and will be removed in Twig 2.0.', E_USER_DEPRECATED); + + $this->bcGetCacheFilename = true; + } + } } /** @@ -132,6 +165,7 @@ class Twig_Environment public function setBaseTemplateClass($class) { $this->baseTemplateClass = $class; + $this->updateOptionsHash(); } /** @@ -140,6 +174,7 @@ class Twig_Environment public function enableDebug() { $this->debug = true; + $this->updateOptionsHash(); } /** @@ -148,6 +183,7 @@ class Twig_Environment public function disableDebug() { $this->debug = false; + $this->updateOptionsHash(); } /** @@ -192,6 +228,7 @@ class Twig_Environment public function enableStrictVariables() { $this->strictVariables = true; + $this->updateOptionsHash(); } /** @@ -200,6 +237,7 @@ class Twig_Environment public function disableStrictVariables() { $this->strictVariables = false; + $this->updateOptionsHash(); } /** @@ -213,24 +251,43 @@ class Twig_Environment } /** - * Gets the cache directory or false if cache is disabled. + * Gets the current cache implementation. * - * @return string|false + * @param bool $original Whether to return the original cache option or the real cache instance + * + * @return Twig_CacheInterface|string|false A Twig_CacheInterface implementation, + * an absolute path to the compiled templates, + * or false to disable cache */ - public function getCache() + public function getCache($original = true) { - return $this->cache; + return $original ? $this->originalCache : $this->cache; } /** - * Sets the cache directory or false if cache is disabled. + * Sets the current cache implementation. * - * @param string|false $cache The absolute path to the compiled templates, - * or false to disable cache + * @param Twig_CacheInterface|string|false $cache A Twig_CacheInterface implementation, + * an absolute path to the compiled templates, + * or false to disable cache */ public function setCache($cache) { - $this->cache = $cache ? $cache : false; + if (is_string($cache)) { + $this->originalCache = $cache; + $this->cache = new Twig_Cache_Filesystem($cache); + } elseif (false === $cache) { + $this->originalCache = $cache; + $this->cache = new Twig_Cache_Null(); + } elseif (null === $cache) { + @trigger_error('Using "null" as the cache strategy is deprecated since version 1.23 and will be removed in Twig 2.0.', E_USER_DEPRECATED); + $this->originalCache = false; + $this->cache = new Twig_Cache_Null(); + } elseif ($cache instanceof Twig_CacheInterface) { + $this->originalCache = $this->cache = $cache; + } else { + throw new LogicException(sprintf('Cache can only be a string, false, or a Twig_CacheInterface implementation.')); + } } /** @@ -239,38 +296,53 @@ class Twig_Environment * @param string $name The template name * * @return string|false The cache file name or false when caching is disabled + * + * @deprecated since 1.22 (to be removed in 2.0) */ public function getCacheFilename($name) { - if (false === $this->cache) { - return false; - } + @trigger_error(sprintf('The %s method is deprecated since version 1.22 and will be removed in Twig 2.0.', __METHOD__), E_USER_DEPRECATED); - $class = substr($this->getTemplateClass($name), strlen($this->templateClassPrefix)); + $key = $this->cache->generateKey($name, $this->getTemplateClass($name)); - return $this->getCache().'/'.$class[0].'/'.$class[1].'/'.$class.'.php'; + return !$key ? false : $key; } /** * Gets the template class associated with the given string. * - * @param string $name The name for which to calculate the template class name - * @param int $index The index if it is an embedded template + * The generated template class is based on the following parameters: + * + * * The cache key for the given template; + * * The currently enabled extensions; + * * Whether the Twig C extension is available or not; + * * PHP version; + * * Twig version; + * * Options with what environment was created. + * + * @param string $name The name for which to calculate the template class name + * @param int|null $index The index if it is an embedded template * * @return string The template class name */ public function getTemplateClass($name, $index = null) { - return $this->templateClassPrefix.hash('sha256', $this->getLoader()->getCacheKey($name)).(null === $index ? '' : '_'.$index); + $key = $this->getLoader()->getCacheKey($name).$this->optionsHash; + + return $this->templateClassPrefix.hash('sha256', $key).(null === $index ? '' : '_'.$index); } /** * Gets the template class prefix. * * @return string The template class prefix + * + * @deprecated since 1.22 (to be removed in 2.0) */ public function getTemplateClassPrefix() { + @trigger_error(sprintf('The %s method is deprecated since version 1.22 and will be removed in Twig 2.0.', __METHOD__), E_USER_DEPRECATED); + return $this->templateClassPrefix; } @@ -307,33 +379,97 @@ class Twig_Environment } /** - * Loads a template by name. + * Loads a template. + * + * @param string|Twig_TemplateWrapper|Twig_Template $name The template name + * + * @throws Twig_Error_Loader When the template cannot be found + * @throws Twig_Error_Runtime When a previously generated cache is corrupted + * @throws Twig_Error_Syntax When an error occurred during compilation + * + * @return Twig_TemplateWrapper + */ + public function load($name) + { + if ($name instanceof Twig_TemplateWrapper) { + return $name; + } + + if ($name instanceof Twig_Template) { + return new Twig_TemplateWrapper($this, $name); + } + + return new Twig_TemplateWrapper($this, $this->loadTemplate($name)); + } + + /** + * Loads a template internal representation. + * + * This method is for internal use only and should never be called + * directly. * * @param string $name The template name * @param int $index The index if it is an embedded template * * @return Twig_TemplateInterface A template instance representing the given template name * - * @throws Twig_Error_Loader When the template cannot be found - * @throws Twig_Error_Syntax When an error occurred during compilation + * @throws Twig_Error_Loader When the template cannot be found + * @throws Twig_Error_Runtime When a previously generated cache is corrupted + * @throws Twig_Error_Syntax When an error occurred during compilation + * + * @internal */ public function loadTemplate($name, $index = null) { - $cls = $this->getTemplateClass($name, $index); + $cls = $mainCls = $this->getTemplateClass($name); + if (null !== $index) { + $cls .= '_'.$index; + } if (isset($this->loadedTemplates[$cls])) { return $this->loadedTemplates[$cls]; } if (!class_exists($cls, false)) { - if (false === $cache = $this->getCacheFilename($name)) { - eval('?>'.$this->compileSource($this->getLoader()->getSource($name), $name)); + if ($this->bcGetCacheFilename) { + $key = $this->getCacheFilename($name); } else { - if (!is_file($cache) || ($this->isAutoReload() && !$this->isTemplateFresh($name, filemtime($cache)))) { - $this->writeCacheFile($cache, $this->compileSource($this->getLoader()->getSource($name), $name)); + $key = $this->cache->generateKey($name, $mainCls); + } + + if (!$this->isAutoReload() || $this->isTemplateFresh($name, $this->cache->getTimestamp($key))) { + $this->cache->load($key); + } + + if (!class_exists($cls, false)) { + $loader = $this->getLoader(); + if (!$loader instanceof Twig_SourceContextLoaderInterface) { + $source = new Twig_Source($loader->getSource($name), $name); + } else { + $source = $loader->getSourceContext($name); } - require_once $cache; + $content = $this->compileSource($source); + + if ($this->bcWriteCacheFile) { + $this->writeCacheFile($key, $content); + } else { + $this->cache->write($key, $content); + $this->cache->load($key); + } + + if (!class_exists($mainCls, false)) { + /* Last line of defense if either $this->bcWriteCacheFile was used, + * $this->cache is implemented as a no-op or we have a race condition + * where the cache was cleared between the above calls to write to and load from + * the cache. + */ + eval('?>'.$content); + } + } + + if (!class_exists($cls, false)) { + throw new Twig_Error_Runtime(sprintf('Failed to load Twig template "%s", index "%s": cache is corrupted.', $name, $index), -1, $source); } } @@ -341,7 +477,22 @@ class Twig_Environment $this->initRuntime(); } - return $this->loadedTemplates[$cls] = new $cls($this); + if (isset($this->loading[$cls])) { + throw new Twig_Error_Runtime(sprintf('Circular reference detected for Twig template "%s", path: %s.', $name, implode(' -> ', array_merge($this->loading, array($name))))); + } + + $this->loading[$cls] = $name; + + try { + $this->loadedTemplates[$cls] = new $cls($this); + unset($this->loading[$cls]); + } catch (\Exception $e) { + unset($this->loading[$cls]); + + throw $e; + } + + return $this->loadedTemplates[$cls]; } /** @@ -358,7 +509,7 @@ class Twig_Environment */ public function createTemplate($template) { - $name = sprintf('__string_template__%s', hash('sha256', uniqid(mt_rand(), true), false)); + $name = sprintf('__string_template__%s', hash('sha256', $template, false)); $loader = new Twig_Loader_Chain(array( new Twig_Loader_Array(array($name => $template)), @@ -371,6 +522,10 @@ class Twig_Environment } catch (Exception $e) { $this->setLoader($current); + throw $e; + } catch (Throwable $e) { + $this->setLoader($current); + throw $e; } $this->setLoader($current); @@ -392,14 +547,16 @@ class Twig_Environment */ public function isTemplateFresh($name, $time) { - foreach ($this->extensions as $extension) { - $r = new ReflectionObject($extension); - if (filemtime($r->getFileName()) > $time) { - return false; + if (0 === $this->lastModifiedExtension) { + foreach ($this->extensions as $extension) { + $r = new ReflectionObject($extension); + if (file_exists($r->getFileName()) && ($extensionTime = filemtime($r->getFileName())) > $this->lastModifiedExtension) { + $this->lastModifiedExtension = $extensionTime; + } } } - return $this->getLoader()->isFresh($name, $time); + return $this->lastModifiedExtension <= $time && $this->getLoader()->isFresh($name, $time); } /** @@ -446,23 +603,25 @@ class Twig_Environment */ public function clearTemplateCache() { - @trigger_error(sprintf('The %s method is deprecated and will be removed in Twig 2.0.', __METHOD__), E_USER_DEPRECATED); + @trigger_error(sprintf('The %s method is deprecated since version 1.18.3 and will be removed in Twig 2.0.', __METHOD__), E_USER_DEPRECATED); $this->loadedTemplates = array(); } /** * Clears the template cache files on the filesystem. + * + * @deprecated since 1.22 (to be removed in 2.0) */ public function clearCacheFiles() { - if (false === $this->cache) { - return; - } + @trigger_error(sprintf('The %s method is deprecated since version 1.22 and will be removed in Twig 2.0.', __METHOD__), E_USER_DEPRECATED); - foreach (new RecursiveIteratorIterator(new RecursiveDirectoryIterator($this->cache), RecursiveIteratorIterator::LEAVES_ONLY) as $file) { - if ($file->isFile()) { - @unlink($file->getPathname()); + if (is_string($this->originalCache)) { + foreach (new RecursiveIteratorIterator(new RecursiveDirectoryIterator($this->originalCache), RecursiveIteratorIterator::LEAVES_ONLY) as $file) { + if ($file->isFile()) { + @unlink($file->getPathname()); + } } } } @@ -470,10 +629,14 @@ class Twig_Environment /** * Gets the Lexer instance. * - * @return Twig_LexerInterface A Twig_LexerInterface instance + * @return Twig_LexerInterface + * + * @deprecated since 1.25 (to be removed in 2.0) */ public function getLexer() { + @trigger_error(sprintf('The %s() method is deprecated since version 1.25 and will be removed in 2.0.', __FUNCTION__), E_USER_DEPRECATED); + if (null === $this->lexer) { $this->lexer = new Twig_Lexer($this); } @@ -481,11 +644,6 @@ class Twig_Environment return $this->lexer; } - /** - * Sets the Lexer instance. - * - * @param Twig_LexerInterface $lexer A Twig_LexerInterface instance - */ public function setLexer(Twig_LexerInterface $lexer) { $this->lexer = $lexer; @@ -494,25 +652,38 @@ class Twig_Environment /** * Tokenizes a source code. * - * @param string $source The template source code - * @param string $name The template name + * @param string|Twig_Source $source The template source code + * @param string $name The template name (deprecated) * - * @return Twig_TokenStream A Twig_TokenStream instance + * @return Twig_TokenStream * * @throws Twig_Error_Syntax When the code is syntactically wrong */ public function tokenize($source, $name = null) { - return $this->getLexer()->tokenize($source, $name); + if (!$source instanceof Twig_Source) { + @trigger_error(sprintf('Passing a string as the $source argument of %s() is deprecated since version 1.27. Pass a Twig_Source instance instead.', __METHOD__), E_USER_DEPRECATED); + $source = new Twig_Source($source, $name); + } + + if (null === $this->lexer) { + $this->lexer = new Twig_Lexer($this); + } + + return $this->lexer->tokenize($source); } /** * Gets the Parser instance. * - * @return Twig_ParserInterface A Twig_ParserInterface instance + * @return Twig_ParserInterface + * + * @deprecated since 1.25 (to be removed in 2.0) */ public function getParser() { + @trigger_error(sprintf('The %s() method is deprecated since version 1.25 and will be removed in 2.0.', __FUNCTION__), E_USER_DEPRECATED); + if (null === $this->parser) { $this->parser = new Twig_Parser($this); } @@ -520,11 +691,6 @@ class Twig_Environment return $this->parser; } - /** - * Sets the Parser instance. - * - * @param Twig_ParserInterface $parser A Twig_ParserInterface instance - */ public function setParser(Twig_ParserInterface $parser) { $this->parser = $parser; @@ -533,24 +699,30 @@ class Twig_Environment /** * Converts a token stream to a node tree. * - * @param Twig_TokenStream $stream A token stream instance - * - * @return Twig_Node_Module A node tree + * @return Twig_Node_Module * * @throws Twig_Error_Syntax When the token stream is syntactically or semantically wrong */ public function parse(Twig_TokenStream $stream) { - return $this->getParser()->parse($stream); + if (null === $this->parser) { + $this->parser = new Twig_Parser($this); + } + + return $this->parser->parse($stream); } /** * Gets the Compiler instance. * - * @return Twig_CompilerInterface A Twig_CompilerInterface instance + * @return Twig_CompilerInterface + * + * @deprecated since 1.25 (to be removed in 2.0) */ public function getCompiler() { + @trigger_error(sprintf('The %s() method is deprecated since version 1.25 and will be removed in 2.0.', __FUNCTION__), E_USER_DEPRECATED); + if (null === $this->compiler) { $this->compiler = new Twig_Compiler($this); } @@ -558,11 +730,6 @@ class Twig_Environment return $this->compiler; } - /** - * Sets the Compiler instance. - * - * @param Twig_CompilerInterface $compiler A Twig_CompilerInterface instance - */ public function setCompiler(Twig_CompilerInterface $compiler) { $this->compiler = $compiler; @@ -571,20 +738,22 @@ class Twig_Environment /** * Compiles a node and returns the PHP code. * - * @param Twig_NodeInterface $node A Twig_NodeInterface instance - * * @return string The compiled PHP source code */ public function compile(Twig_NodeInterface $node) { - return $this->getCompiler()->compile($node)->getSource(); + if (null === $this->compiler) { + $this->compiler = new Twig_Compiler($this); + } + + return $this->compiler->compile($node)->getSource(); } /** * Compiles a template source code. * - * @param string $source The template source code - * @param string $name The template name + * @param string|Twig_Source $source The template source code + * @param string $name The template name (deprecated) * * @return string The compiled PHP source code * @@ -592,30 +761,34 @@ class Twig_Environment */ public function compileSource($source, $name = null) { + if (!$source instanceof Twig_Source) { + @trigger_error(sprintf('Passing a string as the $source argument of %s() is deprecated since version 1.27. Pass a Twig_Source instance instead.', __METHOD__), E_USER_DEPRECATED); + $source = new Twig_Source($source, $name); + } + try { - return $this->compile($this->parse($this->tokenize($source, $name))); + return $this->compile($this->parse($this->tokenize($source))); } catch (Twig_Error $e) { - $e->setTemplateFile($name); + $e->setSourceContext($source); throw $e; } catch (Exception $e) { - throw new Twig_Error_Syntax(sprintf('An exception has been thrown during the compilation of a template ("%s").', $e->getMessage()), -1, $name, $e); + throw new Twig_Error_Syntax(sprintf('An exception has been thrown during the compilation of a template ("%s").', $e->getMessage()), -1, $source, $e); } } - /** - * Sets the Loader instance. - * - * @param Twig_LoaderInterface $loader A Twig_LoaderInterface instance - */ public function setLoader(Twig_LoaderInterface $loader) { + if (!$loader instanceof Twig_SourceContextLoaderInterface && 0 !== strpos(get_class($loader), 'Mock_')) { + @trigger_error(sprintf('Twig loader "%s" should implement Twig_SourceContextLoaderInterface since version 1.27.', get_class($loader)), E_USER_DEPRECATED); + } + $this->loader = $loader; } /** * Gets the Loader instance. * - * @return Twig_LoaderInterface A Twig_LoaderInterface instance + * @return Twig_LoaderInterface */ public function getLoader() { @@ -648,12 +821,22 @@ class Twig_Environment /** * Initializes the runtime environment. + * + * @deprecated since 1.23 (to be removed in 2.0) */ public function initRuntime() { $this->runtimeInitialized = true; - foreach ($this->getExtensions() as $extension) { + foreach ($this->getExtensions() as $name => $extension) { + if (!$extension instanceof Twig_Extension_InitRuntimeInterface) { + $m = new ReflectionMethod($extension, 'initRuntime'); + + if ('Twig_Extension' !== $m->getDeclaringClass()->getName()) { + @trigger_error(sprintf('Defining the initRuntime() method in the "%s" extension is deprecated since version 1.23. Use the `needs_environment` option to get the Twig_Environment instance in filters, functions, or tests; or explicitly implement Twig_Extension_InitRuntimeInterface if needed (not recommended).', $name), E_USER_DEPRECATED); + } + } + $extension->initRuntime($this); } } @@ -661,43 +844,111 @@ class Twig_Environment /** * Returns true if the given extension is registered. * - * @param string $name The extension name + * @param string $class The extension class name * * @return bool Whether the extension is registered or not */ - public function hasExtension($name) + public function hasExtension($class) { - return isset($this->extensions[$name]); - } - - /** - * Gets an extension by name. - * - * @param string $name The extension name - * - * @return Twig_ExtensionInterface A Twig_ExtensionInterface instance - */ - public function getExtension($name) - { - if (!isset($this->extensions[$name])) { - throw new Twig_Error_Runtime(sprintf('The "%s" extension is not enabled.', $name)); + $class = ltrim($class, '\\'); + if (!isset($this->extensionsByClass[$class]) && class_exists($class, false)) { + // For BC/FC with namespaced aliases + $class = new ReflectionClass($class); + $class = $class->name; } - return $this->extensions[$name]; + if (isset($this->extensions[$class])) { + if ($class !== get_class($this->extensions[$class])) { + @trigger_error(sprintf('Referencing the "%s" extension by its name (defined by getName()) is deprecated since 1.26 and will be removed in Twig 2.0. Use the Fully Qualified Extension Class Name instead.', $class), E_USER_DEPRECATED); + } + + return true; + } + + return isset($this->extensionsByClass[$class]); } /** - * Registers an extension. - * - * @param Twig_ExtensionInterface $extension A Twig_ExtensionInterface instance + * Adds a runtime loader. */ + public function addRuntimeLoader(Twig_RuntimeLoaderInterface $loader) + { + $this->runtimeLoaders[] = $loader; + } + + /** + * Gets an extension by class name. + * + * @param string $class The extension class name + * + * @return Twig_ExtensionInterface + */ + public function getExtension($class) + { + $class = ltrim($class, '\\'); + if (!isset($this->extensionsByClass[$class]) && class_exists($class, false)) { + // For BC/FC with namespaced aliases + $class = new ReflectionClass($class); + $class = $class->name; + } + + if (isset($this->extensions[$class])) { + if ($class !== get_class($this->extensions[$class])) { + @trigger_error(sprintf('Referencing the "%s" extension by its name (defined by getName()) is deprecated since 1.26 and will be removed in Twig 2.0. Use the Fully Qualified Extension Class Name instead.', $class), E_USER_DEPRECATED); + } + + return $this->extensions[$class]; + } + + if (!isset($this->extensionsByClass[$class])) { + throw new Twig_Error_Runtime(sprintf('The "%s" extension is not enabled.', $class)); + } + + return $this->extensionsByClass[$class]; + } + + /** + * Returns the runtime implementation of a Twig element (filter/function/test). + * + * @param string $class A runtime class name + * + * @return object The runtime implementation + * + * @throws Twig_Error_Runtime When the template cannot be found + */ + public function getRuntime($class) + { + if (isset($this->runtimes[$class])) { + return $this->runtimes[$class]; + } + + foreach ($this->runtimeLoaders as $loader) { + if (null !== $runtime = $loader->load($class)) { + return $this->runtimes[$class] = $runtime; + } + } + + throw new Twig_Error_Runtime(sprintf('Unable to load the "%s" runtime.', $class)); + } + public function addExtension(Twig_ExtensionInterface $extension) { if ($this->extensionInitialized) { throw new LogicException(sprintf('Unable to register extension "%s" as extensions have already been initialized.', $extension->getName())); } + $class = get_class($extension); + if ($class !== $extension->getName()) { + if (isset($this->extensions[$extension->getName()])) { + unset($this->extensions[$extension->getName()], $this->extensionsByClass[$class]); + @trigger_error(sprintf('The possibility to register the same extension twice ("%s") is deprecated since version 1.23 and will be removed in Twig 2.0. Use proper PHP inheritance instead.', $extension->getName()), E_USER_DEPRECATED); + } + } + + $this->lastModifiedExtension = 0; + $this->extensionsByClass[$class] = $extension; $this->extensions[$extension->getName()] = $extension; + $this->updateOptionsHash(); } /** @@ -711,13 +962,29 @@ class Twig_Environment */ public function removeExtension($name) { - @trigger_error(sprintf('The %s method is deprecated and will be removed in Twig 2.0.', __METHOD__), E_USER_DEPRECATED); + @trigger_error(sprintf('The %s method is deprecated since version 1.12 and will be removed in Twig 2.0.', __METHOD__), E_USER_DEPRECATED); if ($this->extensionInitialized) { throw new LogicException(sprintf('Unable to remove extension "%s" as extensions have already been initialized.', $name)); } - unset($this->extensions[$name]); + $class = ltrim($name, '\\'); + if (!isset($this->extensionsByClass[$class]) && class_exists($class, false)) { + // For BC/FC with namespaced aliases + $class = new ReflectionClass($class); + $class = $class->name; + } + + if (isset($this->extensions[$class])) { + if ($class !== get_class($this->extensions[$class])) { + @trigger_error(sprintf('Referencing the "%s" extension by its name (defined by getName()) is deprecated since 1.26 and will be removed in Twig 2.0. Use the Fully Qualified Extension Class Name instead.', $class), E_USER_DEPRECATED); + } + + unset($this->extensions[$class]); + } + + unset($this->extensions[$class]); + $this->updateOptionsHash(); } /** @@ -735,18 +1002,13 @@ class Twig_Environment /** * Returns all registered extensions. * - * @return array An array of extensions + * @return Twig_ExtensionInterface[] An array of extensions (keys are for internal usage only and should not be relied on) */ public function getExtensions() { return $this->extensions; } - /** - * Registers a Token Parser. - * - * @param Twig_TokenParserInterface $parser A Twig_TokenParserInterface instance - */ public function addTokenParser(Twig_TokenParserInterface $parser) { if ($this->extensionInitialized) { @@ -759,7 +1021,9 @@ class Twig_Environment /** * Gets the registered Token Parsers. * - * @return Twig_TokenParserBrokerInterface A broker containing token parsers + * @return Twig_TokenParserBrokerInterface + * + * @internal */ public function getTokenParsers() { @@ -775,7 +1039,9 @@ class Twig_Environment * * Be warned that this method cannot return tags defined by Twig_TokenParserBrokerInterface classes. * - * @return Twig_TokenParserInterface[] An array of Twig_TokenParserInterface instances + * @return Twig_TokenParserInterface[] + * + * @internal */ public function getTags() { @@ -789,11 +1055,6 @@ class Twig_Environment return $tags; } - /** - * Registers a Node Visitor. - * - * @param Twig_NodeVisitorInterface $visitor A Twig_NodeVisitorInterface instance - */ public function addNodeVisitor(Twig_NodeVisitorInterface $visitor) { if ($this->extensionInitialized) { @@ -806,7 +1067,9 @@ class Twig_Environment /** * Gets the registered Node Visitors. * - * @return Twig_NodeVisitorInterface[] An array of Twig_NodeVisitorInterface instances + * @return Twig_NodeVisitorInterface[] + * + * @internal */ public function getNodeVisitors() { @@ -821,19 +1084,19 @@ class Twig_Environment * Registers a Filter. * * @param string|Twig_SimpleFilter $name The filter name or a Twig_SimpleFilter instance - * @param Twig_FilterInterface|Twig_SimpleFilter $filter A Twig_FilterInterface instance or a Twig_SimpleFilter instance + * @param Twig_FilterInterface|Twig_SimpleFilter $filter */ public function addFilter($name, $filter = null) { if (!$name instanceof Twig_SimpleFilter && !($filter instanceof Twig_SimpleFilter || $filter instanceof Twig_FilterInterface)) { - throw new LogicException('A filter must be an instance of Twig_FilterInterface or Twig_SimpleFilter'); + throw new LogicException('A filter must be an instance of Twig_FilterInterface or Twig_SimpleFilter.'); } if ($name instanceof Twig_SimpleFilter) { $filter = $name; $name = $filter->getName(); } else { - @trigger_error(sprintf('Passing a name as a first argument to the %s method is deprecated. Pass an instance of "Twig_SimpleFilter" instead when defining filter "%s".', __METHOD__, $name), E_USER_DEPRECATED); + @trigger_error(sprintf('Passing a name as a first argument to the %s method is deprecated since version 1.21. Pass an instance of "Twig_SimpleFilter" instead when defining filter "%s".', __METHOD__, $name), E_USER_DEPRECATED); } if ($this->extensionInitialized) { @@ -852,6 +1115,8 @@ class Twig_Environment * @param string $name The filter name * * @return Twig_Filter|false A Twig_Filter instance or false if the filter does not exist + * + * @internal */ public function getFilter($name) { @@ -893,11 +1158,13 @@ class Twig_Environment /** * Gets the registered Filters. * - * Be warned that this method cannot return filters defined with registerUndefinedFunctionCallback. + * Be warned that this method cannot return filters defined with registerUndefinedFilterCallback. * - * @return Twig_FilterInterface[] An array of Twig_FilterInterface instances + * @return Twig_FilterInterface[] * * @see registerUndefinedFilterCallback + * + * @internal */ public function getFilters() { @@ -917,14 +1184,14 @@ class Twig_Environment public function addTest($name, $test = null) { if (!$name instanceof Twig_SimpleTest && !($test instanceof Twig_SimpleTest || $test instanceof Twig_TestInterface)) { - throw new LogicException('A test must be an instance of Twig_TestInterface or Twig_SimpleTest'); + throw new LogicException('A test must be an instance of Twig_TestInterface or Twig_SimpleTest.'); } if ($name instanceof Twig_SimpleTest) { $test = $name; $name = $test->getName(); } else { - @trigger_error(sprintf('Passing a name as a first argument to the %s method is deprecated. Pass an instance of "Twig_SimpleTest" instead when defining test "%s".', __METHOD__, $name), E_USER_DEPRECATED); + @trigger_error(sprintf('Passing a name as a first argument to the %s method is deprecated since version 1.21. Pass an instance of "Twig_SimpleTest" instead when defining test "%s".', __METHOD__, $name), E_USER_DEPRECATED); } if ($this->extensionInitialized) { @@ -937,7 +1204,9 @@ class Twig_Environment /** * Gets the registered Tests. * - * @return Twig_TestInterface[] An array of Twig_TestInterface instances + * @return Twig_TestInterface[] + * + * @internal */ public function getTests() { @@ -954,6 +1223,8 @@ class Twig_Environment * @param string $name The test name * * @return Twig_Test|false A Twig_Test instance or false if the test does not exist + * + * @internal */ public function getTest($name) { @@ -972,19 +1243,19 @@ class Twig_Environment * Registers a Function. * * @param string|Twig_SimpleFunction $name The function name or a Twig_SimpleFunction instance - * @param Twig_FunctionInterface|Twig_SimpleFunction $function A Twig_FunctionInterface instance or a Twig_SimpleFunction instance + * @param Twig_FunctionInterface|Twig_SimpleFunction $function */ public function addFunction($name, $function = null) { if (!$name instanceof Twig_SimpleFunction && !($function instanceof Twig_SimpleFunction || $function instanceof Twig_FunctionInterface)) { - throw new LogicException('A function must be an instance of Twig_FunctionInterface or Twig_SimpleFunction'); + throw new LogicException('A function must be an instance of Twig_FunctionInterface or Twig_SimpleFunction.'); } if ($name instanceof Twig_SimpleFunction) { $function = $name; $name = $function->getName(); } else { - @trigger_error(sprintf('Passing a name as a first argument to the %s method is deprecated. Pass an instance of "Twig_SimpleFunction" instead when defining function "%s".', __METHOD__, $name), E_USER_DEPRECATED); + @trigger_error(sprintf('Passing a name as a first argument to the %s method is deprecated since version 1.21. Pass an instance of "Twig_SimpleFunction" instead when defining function "%s".', __METHOD__, $name), E_USER_DEPRECATED); } if ($this->extensionInitialized) { @@ -1003,6 +1274,8 @@ class Twig_Environment * @param string $name function name * * @return Twig_Function|false A Twig_Function instance or false if the function does not exist + * + * @internal */ public function getFunction($name) { @@ -1046,9 +1319,11 @@ class Twig_Environment * * Be warned that this method cannot return functions defined with registerUndefinedFunctionCallback. * - * @return Twig_FunctionInterface[] An array of Twig_FunctionInterface instances + * @return Twig_FunctionInterface[] * * @see registerUndefinedFunctionCallback + * + * @internal */ public function getFunctions() { @@ -1077,7 +1352,7 @@ class Twig_Environment if (!array_key_exists($name, $this->globals)) { // The deprecation notice must be turned into the following exception in Twig 2.0 - @trigger_error(sprintf('Registering global variable "%s" at runtime or when the extensions have already been initialized is deprecated.', $name), E_USER_DEPRECATED); + @trigger_error(sprintf('Registering global variable "%s" at runtime or when the extensions have already been initialized is deprecated since version 1.21.', $name), E_USER_DEPRECATED); //throw new LogicException(sprintf('Unable to add global "%s" as the runtime or the extensions have already been initialized.', $name)); } } @@ -1094,6 +1369,8 @@ class Twig_Environment * Gets the registered Globals. * * @return array An array of globals + * + * @internal */ public function getGlobals() { @@ -1132,6 +1409,8 @@ class Twig_Environment * Gets the registered unary Operators. * * @return array An array of unary operators + * + * @internal */ public function getUnaryOperators() { @@ -1146,6 +1425,8 @@ class Twig_Environment * Gets the registered binary Operators. * * @return array An array of binary operators + * + * @internal */ public function getBinaryOperators() { @@ -1156,24 +1437,31 @@ class Twig_Environment return $this->binaryOperators; } + /** + * @deprecated since 1.23 (to be removed in 2.0) + */ public function computeAlternatives($name, $items) { - $alternatives = array(); - foreach ($items as $item) { - $lev = levenshtein($name, $item); - if ($lev <= strlen($name) / 3 || false !== strpos($item, $name)) { - $alternatives[$item] = $lev; - } - } - asort($alternatives); + @trigger_error(sprintf('The %s method is deprecated since version 1.23 and will be removed in Twig 2.0.', __METHOD__), E_USER_DEPRECATED); - return array_keys($alternatives); + return Twig_Error_Syntax::computeAlternatives($name, $items); } + /** + * @internal + */ protected function initGlobals() { $globals = array(); - foreach ($this->extensions as $extension) { + foreach ($this->extensions as $name => $extension) { + if (!$extension instanceof Twig_Extension_GlobalsInterface) { + $m = new ReflectionMethod($extension, 'getGlobals'); + + if ('Twig_Extension' !== $m->getDeclaringClass()->getName()) { + @trigger_error(sprintf('Defining the getGlobals() method in the "%s" extension without explicitly implementing Twig_Extension_GlobalsInterface is deprecated since version 1.23.', $name), E_USER_DEPRECATED); + } + } + $extGlob = $extension->getGlobals(); if (!is_array($extGlob)) { throw new UnexpectedValueException(sprintf('"%s::getGlobals()" must return an array of globals.', get_class($extension))); @@ -1187,13 +1475,15 @@ class Twig_Environment return call_user_func_array('array_merge', $globals); } + /** + * @internal + */ protected function initExtensions() { if ($this->extensionInitialized) { return; } - $this->extensionInitialized = true; $this->parsers = new Twig_TokenParserBroker(array(), array(), false); $this->filters = array(); $this->functions = array(); @@ -1206,8 +1496,13 @@ class Twig_Environment $this->initExtension($extension); } $this->initExtension($this->staging); + // Done at the end only, so that an exception during initialization does not mark the environment as initialized when catching the exception + $this->extensionInitialized = true; } + /** + * @internal + */ protected function initExtension(Twig_ExtensionInterface $extension) { // filters @@ -1215,7 +1510,7 @@ class Twig_Environment if ($filter instanceof Twig_SimpleFilter) { $name = $filter->getName(); } else { - @trigger_error(sprintf('Using an instance of "%s" for filter "%s" is deprecated. Use Twig_SimpleFilter instead.', get_class($filter), $name), E_USER_DEPRECATED); + @trigger_error(sprintf('Using an instance of "%s" for filter "%s" is deprecated since version 1.21. Use Twig_SimpleFilter instead.', get_class($filter), $name), E_USER_DEPRECATED); } $this->filters[$name] = $filter; @@ -1226,7 +1521,7 @@ class Twig_Environment if ($function instanceof Twig_SimpleFunction) { $name = $function->getName(); } else { - @trigger_error(sprintf('Using an instance of "%s" for function "%s" is deprecated. Use Twig_SimpleFunction instead.', get_class($function), $name), E_USER_DEPRECATED); + @trigger_error(sprintf('Using an instance of "%s" for function "%s" is deprecated since version 1.21. Use Twig_SimpleFunction instead.', get_class($function), $name), E_USER_DEPRECATED); } $this->functions[$name] = $function; @@ -1237,7 +1532,7 @@ class Twig_Environment if ($test instanceof Twig_SimpleTest) { $name = $test->getName(); } else { - @trigger_error(sprintf('Using an instance of "%s" for test "%s" is deprecated. Use Twig_SimpleTest instead.', get_class($test), $name), E_USER_DEPRECATED); + @trigger_error(sprintf('Using an instance of "%s" for test "%s" is deprecated since version 1.21. Use Twig_SimpleTest instead.', get_class($test), $name), E_USER_DEPRECATED); } $this->tests[$name] = $test; @@ -1248,11 +1543,11 @@ class Twig_Environment if ($parser instanceof Twig_TokenParserInterface) { $this->parsers->addTokenParser($parser); } elseif ($parser instanceof Twig_TokenParserBrokerInterface) { - @trigger_error('Registering a Twig_TokenParserBrokerInterface instance is deprecated.', E_USER_DEPRECATED); + @trigger_error('Registering a Twig_TokenParserBrokerInterface instance is deprecated since version 1.21.', E_USER_DEPRECATED); $this->parsers->addTokenParserBroker($parser); } else { - throw new LogicException('getTokenParsers() must return an array of Twig_TokenParserInterface or Twig_TokenParserBrokerInterface instances'); + throw new LogicException('getTokenParsers() must return an array of Twig_TokenParserInterface or Twig_TokenParserBrokerInterface instances.'); } } @@ -1263,8 +1558,12 @@ class Twig_Environment // operators if ($operators = $extension->getOperators()) { + if (!is_array($operators)) { + throw new InvalidArgumentException(sprintf('"%s::getOperators()" must return an array with operators, got "%s".', get_class($extension), is_object($operators) ? get_class($operators) : gettype($operators).(is_resource($operators) ? '' : '#'.$operators))); + } + if (2 !== count($operators)) { - throw new InvalidArgumentException(sprintf('"%s::getOperators()" does not return a valid operators array.', get_class($extension))); + throw new InvalidArgumentException(sprintf('"%s::getOperators()" must return an array of 2 elements, got %d.', get_class($extension), count($operators))); } $this->unaryOperators = array_merge($this->unaryOperators, $operators[0]); @@ -1272,30 +1571,30 @@ class Twig_Environment } } + /** + * @deprecated since 1.22 (to be removed in 2.0) + */ protected function writeCacheFile($file, $content) { - $dir = dirname($file); - if (!is_dir($dir)) { - if (false === @mkdir($dir, 0777, true)) { - clearstatcache(false, $dir); - if (!is_dir($dir)) { - throw new RuntimeException(sprintf('Unable to create the cache directory (%s).', $dir)); - } - } - } elseif (!is_writable($dir)) { - throw new RuntimeException(sprintf('Unable to write in the cache directory (%s).', $dir)); - } + $this->cache->write($file, $content); + } - $tmpFile = tempnam($dir, basename($file)); - if (false !== @file_put_contents($tmpFile, $content)) { - // rename does not work on Win32 before 5.2.6 - if (@rename($tmpFile, $file) || (@copy($tmpFile, $file) && unlink($tmpFile))) { - @chmod($file, 0666 & ~umask()); - - return; - } - } - - throw new RuntimeException(sprintf('Failed to write cache file "%s".', $file)); + private function updateOptionsHash() + { + $hashParts = array_merge( + array_keys($this->extensions), + array( + (int) function_exists('twig_template_get_attributes'), + PHP_MAJOR_VERSION, + PHP_MINOR_VERSION, + self::VERSION, + (int) $this->debug, + $this->baseTemplateClass, + (int) $this->strictVariables, + ) + ); + $this->optionsHash = implode(':', $hashParts); } } + +class_alias('Twig_Environment', 'Twig\Environment', false); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/Error.php b/lib/silex/vendor/twig/twig/lib/Twig/Error.php index 90650c5fe..787e0d095 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/Error.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/Error.php @@ -3,7 +3,7 @@ /* * This file is part of Twig. * - * (c) 2009 Fabien Potencier + * (c) Fabien Potencier * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -25,8 +25,8 @@ * and line number) yourself by passing them to the constructor. If some or all * these information are not available from where you throw the exception, then * this class will guess them automatically (when the line number is set to -1 - * and/or the filename is set to null). As this is a costly operation, this - * can be disabled by passing false for both the filename and the line number + * and/or the name is set to null). As this is a costly operation, this + * can be disabled by passing false for both the name and the line number * when creating a new instance of this class. * * @author Fabien Potencier @@ -34,29 +34,43 @@ class Twig_Error extends Exception { protected $lineno; + // to be renamed to name in 2.0 protected $filename; protected $rawMessage; protected $previous; + private $sourcePath; + private $sourceCode; + /** * Constructor. * - * Set both the line number and the filename to false to + * Set both the line number and the name to false to * disable automatic guessing of the original template name * and line number. * * Set the line number to -1 to enable its automatic guessing. - * Set the filename to null to enable its automatic guessing. + * Set the name to null to enable its automatic guessing. * * By default, automatic guessing is enabled. * - * @param string $message The error message - * @param int $lineno The template line where the error occurred - * @param string $filename The template file name where the error occurred - * @param Exception $previous The previous exception + * @param string $message The error message + * @param int $lineno The template line where the error occurred + * @param Twig_Source|string|null $source The source context where the error occurred + * @param Exception $previous The previous exception */ - public function __construct($message, $lineno = -1, $filename = null, Exception $previous = null) + public function __construct($message, $lineno = -1, $source = null, Exception $previous = null) { + if (null === $source) { + $name = null; + } elseif (!$source instanceof Twig_Source) { + // for compat with the Twig C ext., passing the template name as string is accepted + $name = $source; + } else { + $name = $source->getName(); + $this->sourceCode = $source->getCode(); + $this->sourcePath = $source->getPath(); + } if (PHP_VERSION_ID < 50300) { $this->previous = $previous; parent::__construct(''); @@ -65,9 +79,9 @@ class Twig_Error extends Exception } $this->lineno = $lineno; - $this->filename = $filename; + $this->filename = $name; - if (-1 === $this->lineno || null === $this->filename) { + if (-1 === $lineno || null === $name || null === $this->sourcePath) { $this->guessTemplateInfo(); } @@ -87,23 +101,62 @@ class Twig_Error extends Exception } /** - * Gets the filename where the error occurred. + * Gets the logical name where the error occurred. * - * @return string The filename + * @return string The name + * + * @deprecated since 1.27 (to be removed in 2.0). Use getSourceContext() instead. */ public function getTemplateFile() { + @trigger_error(sprintf('The "%s" method is deprecated since version 1.27 and will be removed in 2.0. Use getSourceContext() instead.', __METHOD__), E_USER_DEPRECATED); + return $this->filename; } /** - * Sets the filename where the error occurred. + * Sets the logical name where the error occurred. * - * @param string $filename The filename + * @param string $name The name + * + * @deprecated since 1.27 (to be removed in 2.0). Use setSourceContext() instead. */ - public function setTemplateFile($filename) + public function setTemplateFile($name) { - $this->filename = $filename; + @trigger_error(sprintf('The "%s" method is deprecated since version 1.27 and will be removed in 2.0. Use setSourceContext() instead.', __METHOD__), E_USER_DEPRECATED); + + $this->filename = $name; + + $this->updateRepr(); + } + + /** + * Gets the logical name where the error occurred. + * + * @return string The name + * + * @deprecated since 1.29 (to be removed in 2.0). Use getSourceContext() instead. + */ + public function getTemplateName() + { + @trigger_error(sprintf('The "%s" method is deprecated since version 1.29 and will be removed in 2.0. Use getSourceContext() instead.', __METHOD__), E_USER_DEPRECATED); + + return $this->filename; + } + + /** + * Sets the logical name where the error occurred. + * + * @param string $name The name + * + * @deprecated since 1.29 (to be removed in 2.0). Use setSourceContext() instead. + */ + public function setTemplateName($name) + { + @trigger_error(sprintf('The "%s" method is deprecated since version 1.29 and will be removed in 2.0. Use setSourceContext() instead.', __METHOD__), E_USER_DEPRECATED); + + $this->filename = $name; + $this->sourceCode = $this->sourcePath = null; $this->updateRepr(); } @@ -130,6 +183,32 @@ class Twig_Error extends Exception $this->updateRepr(); } + /** + * Gets the source context of the Twig template where the error occurred. + * + * @return Twig_Source|null + */ + public function getSourceContext() + { + return $this->filename ? new Twig_Source($this->sourceCode, $this->filename, $this->sourcePath) : null; + } + + /** + * Sets the source context of the Twig template where the error occurred. + */ + public function setSourceContext(Twig_Source $source = null) + { + if (null === $source) { + $this->sourceCode = $this->filename = $this->sourcePath = null; + } else { + $this->sourceCode = $source->getCode(); + $this->filename = $source->getName(); + $this->sourcePath = $source->getPath(); + } + + $this->updateRepr(); + } + public function guess() { $this->guessTemplateInfo(); @@ -155,23 +234,45 @@ class Twig_Error extends Exception throw new BadMethodCallException(sprintf('Method "Twig_Error::%s()" does not exist.', $method)); } + public function appendMessage($rawMessage) + { + $this->rawMessage .= $rawMessage; + $this->updateRepr(); + } + + /** + * @internal + */ protected function updateRepr() { $this->message = $this->rawMessage; + if ($this->sourcePath && $this->lineno > 0) { + $this->file = $this->sourcePath; + $this->line = $this->lineno; + + return; + } + $dot = false; if ('.' === substr($this->message, -1)) { $this->message = substr($this->message, 0, -1); $dot = true; } + $questionMark = false; + if ('?' === substr($this->message, -1)) { + $this->message = substr($this->message, 0, -1); + $questionMark = true; + } + if ($this->filename) { if (is_string($this->filename) || (is_object($this->filename) && method_exists($this->filename, '__toString'))) { - $filename = sprintf('"%s"', $this->filename); + $name = sprintf('"%s"', $this->filename); } else { - $filename = json_encode($this->filename); + $name = json_encode($this->filename); } - $this->message .= sprintf(' in %s', $filename); + $this->message .= sprintf(' in %s', $name); } if ($this->lineno && $this->lineno >= 0) { @@ -181,8 +282,15 @@ class Twig_Error extends Exception if ($dot) { $this->message .= '.'; } + + if ($questionMark) { + $this->message .= '?'; + } } + /** + * @internal + */ protected function guessTemplateInfo() { $template = null; @@ -205,11 +313,18 @@ class Twig_Error extends Exception } } - // update template filename + // update template name if (null !== $template && null === $this->filename) { $this->filename = $template->getTemplateName(); } + // update template path if any + if (null !== $template && null === $this->sourcePath) { + $src = $template->getSourceContext(); + $this->sourceCode = $src->getCode(); + $this->sourcePath = $src->getPath(); + } + if (null === $template || $this->lineno > -1) { return; } @@ -217,11 +332,6 @@ class Twig_Error extends Exception $r = new ReflectionObject($template); $file = $r->getFileName(); - // hhvm has a bug where eval'ed files comes out as the current directory - if (is_dir($file)) { - $file = ''; - } - $exceptions = array($e = $this); while (($e instanceof self || method_exists($e, 'getPrevious')) && $e = $e->getPrevious()) { $exceptions[] = $e; @@ -248,3 +358,6 @@ class Twig_Error extends Exception } } } + +class_alias('Twig_Error', 'Twig\Error\Error', false); +class_exists('Twig_Source'); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/Error/Loader.php b/lib/silex/vendor/twig/twig/lib/Twig/Error/Loader.php index 68efb574a..df566dd78 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/Error/Loader.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/Error/Loader.php @@ -3,7 +3,7 @@ /* * This file is part of Twig. * - * (c) 2010 Fabien Potencier + * (c) Fabien Potencier * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -24,8 +24,17 @@ */ class Twig_Error_Loader extends Twig_Error { - public function __construct($message, $lineno = -1, $filename = null, Exception $previous = null) + public function __construct($message, $lineno = -1, $source = null, Exception $previous = null) { - parent::__construct($message, false, false, $previous); + if (PHP_VERSION_ID < 50300) { + $this->previous = $previous; + Exception::__construct(''); + } else { + Exception::__construct('', 0, $previous); + } + $this->appendMessage($message); + $this->setTemplateLine(false); } } + +class_alias('Twig_Error_Loader', 'Twig\Error\LoaderError', false); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/Error/Runtime.php b/lib/silex/vendor/twig/twig/lib/Twig/Error/Runtime.php index 8b6ceddb9..3b24ad3a4 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/Error/Runtime.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/Error/Runtime.php @@ -3,8 +3,8 @@ /* * This file is part of Twig. * - * (c) 2009 Fabien Potencier - * (c) 2009 Armin Ronacher + * (c) Fabien Potencier + * (c) Armin Ronacher * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -18,3 +18,5 @@ class Twig_Error_Runtime extends Twig_Error { } + +class_alias('Twig_Error_Runtime', 'Twig\Error\RuntimeError', false); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/Error/Syntax.php b/lib/silex/vendor/twig/twig/lib/Twig/Error/Syntax.php index 0f5c57928..9d09f2179 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/Error/Syntax.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/Error/Syntax.php @@ -3,8 +3,8 @@ /* * This file is part of Twig. * - * (c) 2009 Fabien Potencier - * (c) 2009 Armin Ronacher + * (c) Fabien Potencier + * (c) Armin Ronacher * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -17,4 +17,39 @@ */ class Twig_Error_Syntax extends Twig_Error { + /** + * Tweaks the error message to include suggestions. + * + * @param string $name The original name of the item that does not exist + * @param array $items An array of possible items + */ + public function addSuggestions($name, array $items) + { + if (!$alternatives = self::computeAlternatives($name, $items)) { + return; + } + + $this->appendMessage(sprintf(' Did you mean "%s"?', implode('", "', $alternatives))); + } + + /** + * @internal + * + * To be merged with the addSuggestions() method in 2.0. + */ + public static function computeAlternatives($name, $items) + { + $alternatives = array(); + foreach ($items as $item) { + $lev = levenshtein($name, $item); + if ($lev <= strlen($name) / 3 || false !== strpos($item, $name)) { + $alternatives[$item] = $lev; + } + } + asort($alternatives); + + return array_keys($alternatives); + } } + +class_alias('Twig_Error_Syntax', 'Twig\Error\SyntaxError', false); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/ExistsLoaderInterface.php b/lib/silex/vendor/twig/twig/lib/Twig/ExistsLoaderInterface.php index b168c3c35..968cb21a4 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/ExistsLoaderInterface.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/ExistsLoaderInterface.php @@ -3,7 +3,7 @@ /* * This file is part of Twig. * - * (c) 2009 Fabien Potencier + * (c) Fabien Potencier * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -27,3 +27,5 @@ interface Twig_ExistsLoaderInterface */ public function exists($name); } + +class_alias('Twig_ExistsLoaderInterface', 'Twig\Loader\ExistsLoaderInterface', false); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/ExpressionParser.php b/lib/silex/vendor/twig/twig/lib/Twig/ExpressionParser.php index 322976c60..4906f903e 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/ExpressionParser.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/ExpressionParser.php @@ -3,8 +3,8 @@ /* * This file is part of Twig. * - * (c) 2009 Fabien Potencier - * (c) 2009 Armin Ronacher + * (c) Fabien Potencier + * (c) Armin Ronacher * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -19,6 +19,8 @@ * @see http://en.wikipedia.org/wiki/Operator-precedence_parser * * @author Fabien Potencier + * + * @internal */ class Twig_ExpressionParser { @@ -29,11 +31,23 @@ class Twig_ExpressionParser protected $unaryOperators; protected $binaryOperators; - public function __construct(Twig_Parser $parser, array $unaryOperators, array $binaryOperators) + private $env; + + public function __construct(Twig_Parser $parser, $env = null) { $this->parser = $parser; - $this->unaryOperators = $unaryOperators; - $this->binaryOperators = $binaryOperators; + + if ($env instanceof Twig_Environment) { + $this->env = $env; + $this->unaryOperators = $env->getUnaryOperators(); + $this->binaryOperators = $env->getBinaryOperators(); + } else { + @trigger_error('Passing the operators as constructor arguments to '.__METHOD__.' is deprecated since version 1.27. Pass the environment instead.', E_USER_DEPRECATED); + + $this->env = $parser->getEnvironment(); + $this->unaryOperators = func_get_arg(1); + $this->binaryOperators = func_get_arg(2); + } } public function parseExpression($precedence = 0) @@ -44,7 +58,11 @@ class Twig_ExpressionParser $op = $this->binaryOperators[$token->getValue()]; $this->parser->getStream()->next(); - if (isset($op['callable'])) { + if ('is not' === $token->getValue()) { + $expr = $this->parseNotTestExpression($expr); + } elseif ('is' === $token->getValue()) { + $expr = $this->parseTestExpression($expr); + } elseif (isset($op['callable'])) { $expr = call_user_func($op['callable'], $this->parser, $expr); } else { $expr1 = $this->parseExpression(self::OPERATOR_LEFT === $op['associativity'] ? $op['precedence'] + 1 : $op['precedence']); @@ -171,7 +189,7 @@ class Twig_ExpressionParser $negClass = 'Twig_Node_Expression_Unary_Neg'; $posClass = 'Twig_Node_Expression_Unary_Pos'; if (!(in_array($ref->getName(), array($negClass, $posClass)) || $ref->isSubclassOf($negClass) || $ref->isSubclassOf($posClass))) { - throw new Twig_Error_Syntax(sprintf('Unexpected unary operator "%s"', $token->getValue()), $token->getLine(), $this->parser->getFilename()); + throw new Twig_Error_Syntax(sprintf('Unexpected unary operator "%s".', $token->getValue()), $token->getLine(), $this->parser->getStream()->getSourceContext()); } $this->parser->getStream()->next(); @@ -187,7 +205,7 @@ class Twig_ExpressionParser } elseif ($token->test(Twig_Token::PUNCTUATION_TYPE, '{')) { $node = $this->parseHashExpression(); } else { - throw new Twig_Error_Syntax(sprintf('Unexpected token "%s" of value "%s"', Twig_Token::typeToEnglish($token->getType()), $token->getValue()), $token->getLine(), $this->parser->getFilename()); + throw new Twig_Error_Syntax(sprintf('Unexpected token "%s" of value "%s".', Twig_Token::typeToEnglish($token->getType()), $token->getValue()), $token->getLine(), $this->parser->getStream()->getSourceContext()); } } @@ -216,7 +234,7 @@ class Twig_ExpressionParser $expr = array_shift($nodes); foreach ($nodes as $node) { - $expr = new Twig_Node_Expression_Binary_Concat($expr, $node, $node->getLine()); + $expr = new Twig_Node_Expression_Binary_Concat($expr, $node, $node->getTemplateLine()); } return $expr; @@ -278,7 +296,7 @@ class Twig_ExpressionParser } else { $current = $stream->getCurrent(); - throw new Twig_Error_Syntax(sprintf('A hash key must be a quoted string, a number, a name, or an expression enclosed in parentheses (unexpected token "%s" of value "%s"', Twig_Token::typeToEnglish($current->getType()), $current->getValue()), $current->getLine(), $this->parser->getFilename()); + throw new Twig_Error_Syntax(sprintf('A hash key must be a quoted string, a number, a name, or an expression enclosed in parentheses (unexpected token "%s" of value "%s".', Twig_Token::typeToEnglish($current->getType()), $current->getValue()), $current->getLine(), $stream->getSourceContext()); } $stream->expect(Twig_Token::PUNCTUATION_TYPE, ':', 'A hash key must be followed by a colon (:)'); @@ -317,20 +335,25 @@ class Twig_ExpressionParser case 'parent': $this->parseArguments(); if (!count($this->parser->getBlockStack())) { - throw new Twig_Error_Syntax('Calling "parent" outside a block is forbidden', $line, $this->parser->getFilename()); + throw new Twig_Error_Syntax('Calling "parent" outside a block is forbidden.', $line, $this->parser->getStream()->getSourceContext()); } if (!$this->parser->getParent() && !$this->parser->hasTraits()) { - throw new Twig_Error_Syntax('Calling "parent" on a template that does not extend nor "use" another template is forbidden', $line, $this->parser->getFilename()); + throw new Twig_Error_Syntax('Calling "parent" on a template that does not extend nor "use" another template is forbidden.', $line, $this->parser->getStream()->getSourceContext()); } return new Twig_Node_Expression_Parent($this->parser->peekBlockStack(), $line); case 'block': - return new Twig_Node_Expression_BlockReference($this->parseArguments()->getNode(0), false, $line); + $args = $this->parseArguments(); + if (count($args) < 1) { + throw new Twig_Error_Syntax('The "block" function takes one argument (the block name).', $line, $this->parser->getStream()->getSourceContext()); + } + + return new Twig_Node_Expression_BlockReference($args->getNode(0), count($args) > 1 ? $args->getNode(1) : null, $line); case 'attribute': $args = $this->parseArguments(); if (count($args) < 2) { - throw new Twig_Error_Syntax('The "attribute" function takes at least two arguments (the variable and the attributes)', $line, $this->parser->getFilename()); + throw new Twig_Error_Syntax('The "attribute" function takes at least two arguments (the variable and the attributes).', $line, $this->parser->getStream()->getSourceContext()); } return new Twig_Node_Expression_GetAttr($args->getNode(0), $args->getNode(1), count($args) > 2 ? $args->getNode(2) : null, Twig_Template::ANY_CALL, $line); @@ -373,24 +396,24 @@ class Twig_ExpressionParser $arg = new Twig_Node_Expression_Constant($token->getValue(), $lineno); if ($stream->test(Twig_Token::PUNCTUATION_TYPE, '(')) { - $type = Twig_TemplateInterface::METHOD_CALL; + $type = Twig_Template::METHOD_CALL; foreach ($this->parseArguments() as $n) { $arguments->addElement($n); } } } else { - throw new Twig_Error_Syntax('Expected name or number', $lineno, $this->parser->getFilename()); + throw new Twig_Error_Syntax('Expected name or number.', $lineno, $stream->getSourceContext()); } if ($node instanceof Twig_Node_Expression_Name && null !== $this->parser->getImportedSymbol('template', $node->getAttribute('name'))) { if (!$arg instanceof Twig_Node_Expression_Constant) { - throw new Twig_Error_Syntax(sprintf('Dynamic macro names are not supported (called on "%s")', $node->getAttribute('name')), $token->getLine(), $this->parser->getFilename()); + throw new Twig_Error_Syntax(sprintf('Dynamic macro names are not supported (called on "%s").', $node->getAttribute('name')), $token->getLine(), $stream->getSourceContext()); } $name = $arg->getAttribute('value'); if ($this->parser->isReservedMacroName($name)) { - throw new Twig_Error_Syntax(sprintf('"%s" cannot be called as macro as it is a reserved keyword', $name), $token->getLine(), $this->parser->getFilename()); + throw new Twig_Error_Syntax(sprintf('"%s" cannot be called as macro as it is a reserved keyword.', $name), $token->getLine(), $stream->getSourceContext()); } $node = new Twig_Node_Expression_MethodCall($node, 'get'.$name, $arguments, $lineno); @@ -500,7 +523,7 @@ class Twig_ExpressionParser $name = null; if ($namedArguments && $token = $stream->nextIf(Twig_Token::OPERATOR_TYPE, '=')) { if (!$value instanceof Twig_Node_Expression_Name) { - throw new Twig_Error_Syntax(sprintf('A parameter name must be a string, "%s" given', get_class($value)), $token->getLine(), $this->parser->getFilename()); + throw new Twig_Error_Syntax(sprintf('A parameter name must be a string, "%s" given.', get_class($value)), $token->getLine(), $stream->getSourceContext()); } $name = $value->getAttribute('name'); @@ -508,7 +531,7 @@ class Twig_ExpressionParser $value = $this->parsePrimaryExpression(); if (!$this->checkConstantExpression($value)) { - throw new Twig_Error_Syntax(sprintf('A default value for an argument must be a constant (a boolean, a string, a number, or an array).'), $token->getLine(), $this->parser->getFilename()); + throw new Twig_Error_Syntax(sprintf('A default value for an argument must be a constant (a boolean, a string, a number, or an array).'), $token->getLine(), $stream->getSourceContext()); } } else { $value = $this->parseExpression(); @@ -536,15 +559,17 @@ class Twig_ExpressionParser public function parseAssignmentExpression() { + $stream = $this->parser->getStream(); $targets = array(); while (true) { - $token = $this->parser->getStream()->expect(Twig_Token::NAME_TYPE, null, 'Only variables can be assigned to'); - if (in_array($token->getValue(), array('true', 'false', 'none'))) { - throw new Twig_Error_Syntax(sprintf('You cannot assign a value to "%s"', $token->getValue()), $token->getLine(), $this->parser->getFilename()); + $token = $stream->expect(Twig_Token::NAME_TYPE, null, 'Only variables can be assigned to'); + $value = $token->getValue(); + if (in_array(strtolower($value), array('true', 'false', 'none', 'null'))) { + throw new Twig_Error_Syntax(sprintf('You cannot assign a value to "%s".', $value), $token->getLine(), $stream->getSourceContext()); } - $targets[] = new Twig_Node_Expression_AssignName($token->getValue(), $token->getLine()); + $targets[] = new Twig_Node_Expression_AssignName($value, $token->getLine()); - if (!$this->parser->getStream()->nextIf(Twig_Token::PUNCTUATION_TYPE, ',')) { + if (!$stream->nextIf(Twig_Token::PUNCTUATION_TYPE, ',')) { break; } } @@ -565,25 +590,94 @@ class Twig_ExpressionParser return new Twig_Node($targets); } + private function parseNotTestExpression(Twig_NodeInterface $node) + { + return new Twig_Node_Expression_Unary_Not($this->parseTestExpression($node), $this->parser->getCurrentToken()->getLine()); + } + + private function parseTestExpression(Twig_NodeInterface $node) + { + $stream = $this->parser->getStream(); + list($name, $test) = $this->getTest($node->getTemplateLine()); + + $class = $this->getTestNodeClass($test); + $arguments = null; + if ($stream->test(Twig_Token::PUNCTUATION_TYPE, '(')) { + $arguments = $this->parser->getExpressionParser()->parseArguments(true); + } + + return new $class($node, $name, $arguments, $this->parser->getCurrentToken()->getLine()); + } + + private function getTest($line) + { + $stream = $this->parser->getStream(); + $name = $stream->expect(Twig_Token::NAME_TYPE)->getValue(); + + if ($test = $this->env->getTest($name)) { + return array($name, $test); + } + + if ($stream->test(Twig_Token::NAME_TYPE)) { + // try 2-words tests + $name = $name.' '.$this->parser->getCurrentToken()->getValue(); + + if ($test = $this->env->getTest($name)) { + $stream->next(); + + return array($name, $test); + } + } + + $e = new Twig_Error_Syntax(sprintf('Unknown "%s" test.', $name), $line, $stream->getSourceContext()); + $e->addSuggestions($name, array_keys($this->env->getTests())); + + throw $e; + } + + private function getTestNodeClass($test) + { + if ($test instanceof Twig_SimpleTest && $test->isDeprecated()) { + $stream = $this->parser->getStream(); + $message = sprintf('Twig Test "%s" is deprecated', $test->getName()); + if (!is_bool($test->getDeprecatedVersion())) { + $message .= sprintf(' since version %s', $test->getDeprecatedVersion()); + } + if ($test->getAlternative()) { + $message .= sprintf('. Use "%s" instead', $test->getAlternative()); + } + $src = $stream->getSourceContext(); + $message .= sprintf(' in %s at line %d.', $src->getPath() ? $src->getPath() : $src->getName(), $stream->getCurrent()->getLine()); + + @trigger_error($message, E_USER_DEPRECATED); + } + + if ($test instanceof Twig_SimpleTest) { + return $test->getNodeClass(); + } + + return $test instanceof Twig_Test_Node ? $test->getClass() : 'Twig_Node_Expression_Test'; + } + protected function getFunctionNodeClass($name, $line) { - $env = $this->parser->getEnvironment(); + if (false === $function = $this->env->getFunction($name)) { + $e = new Twig_Error_Syntax(sprintf('Unknown "%s" function.', $name), $line, $this->parser->getStream()->getSourceContext()); + $e->addSuggestions($name, array_keys($this->env->getFunctions())); - if (false === $function = $env->getFunction($name)) { - $message = sprintf('The function "%s" does not exist', $name); - if ($alternatives = $env->computeAlternatives($name, array_keys($env->getFunctions()))) { - $message = sprintf('%s. Did you mean "%s"', $message, implode('", "', $alternatives)); - } - - throw new Twig_Error_Syntax($message, $line, $this->parser->getFilename()); + throw $e; } if ($function instanceof Twig_SimpleFunction && $function->isDeprecated()) { $message = sprintf('Twig Function "%s" is deprecated', $function->getName()); + if (!is_bool($function->getDeprecatedVersion())) { + $message .= sprintf(' since version %s', $function->getDeprecatedVersion()); + } if ($function->getAlternative()) { $message .= sprintf('. Use "%s" instead', $function->getAlternative()); } - $message .= sprintf(' in %s at line %d.', $this->parser->getFilename(), $line); + $src = $this->parser->getStream()->getSourceContext(); + $message .= sprintf(' in %s at line %d.', $src->getPath() ? $src->getPath() : $src->getName(), $line); @trigger_error($message, E_USER_DEPRECATED); } @@ -597,23 +691,23 @@ class Twig_ExpressionParser protected function getFilterNodeClass($name, $line) { - $env = $this->parser->getEnvironment(); + if (false === $filter = $this->env->getFilter($name)) { + $e = new Twig_Error_Syntax(sprintf('Unknown "%s" filter.', $name), $line, $this->parser->getStream()->getSourceContext()); + $e->addSuggestions($name, array_keys($this->env->getFilters())); - if (false === $filter = $env->getFilter($name)) { - $message = sprintf('The filter "%s" does not exist', $name); - if ($alternatives = $env->computeAlternatives($name, array_keys($env->getFilters()))) { - $message = sprintf('%s. Did you mean "%s"', $message, implode('", "', $alternatives)); - } - - throw new Twig_Error_Syntax($message, $line, $this->parser->getFilename()); + throw $e; } if ($filter instanceof Twig_SimpleFilter && $filter->isDeprecated()) { $message = sprintf('Twig Filter "%s" is deprecated', $filter->getName()); + if (!is_bool($filter->getDeprecatedVersion())) { + $message .= sprintf(' since version %s', $filter->getDeprecatedVersion()); + } if ($filter->getAlternative()) { $message .= sprintf('. Use "%s" instead', $filter->getAlternative()); } - $message .= sprintf(' in %s at line %d.', $this->parser->getFilename(), $line); + $src = $this->parser->getStream()->getSourceContext(); + $message .= sprintf(' in %s at line %d.', $src->getPath() ? $src->getPath() : $src->getName(), $line); @trigger_error($message, E_USER_DEPRECATED); } @@ -643,3 +737,5 @@ class Twig_ExpressionParser return true; } } + +class_alias('Twig_ExpressionParser', 'Twig\ExpressionParser', false); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/Extension.php b/lib/silex/vendor/twig/twig/lib/Twig/Extension.php index 5c8ad5c96..38084495f 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/Extension.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/Extension.php @@ -3,91 +3,67 @@ /* * This file is part of Twig. * - * (c) 2009 Fabien Potencier + * (c) Fabien Potencier * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ + abstract class Twig_Extension implements Twig_ExtensionInterface { /** - * Initializes the runtime environment. - * - * This is where you can load some file that contains filter functions for instance. - * - * @param Twig_Environment $environment The current Twig_Environment instance + * @deprecated since 1.23 (to be removed in 2.0), implement Twig_Extension_InitRuntimeInterface instead */ public function initRuntime(Twig_Environment $environment) { } - /** - * Returns the token parser instances to add to the existing list. - * - * @return array An array of Twig_TokenParserInterface or Twig_TokenParserBrokerInterface instances - */ public function getTokenParsers() { return array(); } - /** - * Returns the node visitor instances to add to the existing list. - * - * @return Twig_NodeVisitorInterface[] An array of Twig_NodeVisitorInterface instances - */ public function getNodeVisitors() { return array(); } - /** - * Returns a list of filters to add to the existing list. - * - * @return array An array of filters - */ public function getFilters() { return array(); } - /** - * Returns a list of tests to add to the existing list. - * - * @return array An array of tests - */ public function getTests() { return array(); } - /** - * Returns a list of functions to add to the existing list. - * - * @return array An array of functions - */ public function getFunctions() { return array(); } - /** - * Returns a list of operators to add to the existing list. - * - * @return array An array of operators - */ public function getOperators() { return array(); } /** - * Returns a list of global variables to add to the existing list. - * - * @return array An array of global variables + * @deprecated since 1.23 (to be removed in 2.0), implement Twig_Extension_GlobalsInterface instead */ public function getGlobals() { return array(); } + + /** + * @deprecated since 1.26 (to be removed in 2.0), not used anymore internally + */ + public function getName() + { + return get_class($this); + } } + +class_alias('Twig_Extension', 'Twig\Extension\AbstractExtension', false); +class_exists('Twig_Environment'); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/Extension/Core.php b/lib/silex/vendor/twig/twig/lib/Twig/Extension/Core.php index 4980ceb93..34f9ef7d6 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/Extension/Core.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/Extension/Core.php @@ -1,18 +1,21 @@ numberFormat; } - /** - * Returns the token parser instance to add to the existing list. - * - * @return Twig_TokenParser[] An array of Twig_TokenParser instances - */ public function getTokenParsers() { return array( @@ -137,14 +135,10 @@ class Twig_Extension_Core extends Twig_Extension new Twig_TokenParser_Flush(), new Twig_TokenParser_Do(), new Twig_TokenParser_Embed(), + new Twig_TokenParser_With(), ); } - /** - * Returns a list of filters to add to the existing list. - * - * @return array An array of filters - */ public function getFilters() { $filters = array( @@ -168,7 +162,7 @@ class Twig_Extension_Core extends Twig_Extension new Twig_SimpleFilter('upper', 'strtoupper'), new Twig_SimpleFilter('lower', 'strtolower'), new Twig_SimpleFilter('striptags', 'strip_tags'), - new Twig_SimpleFilter('trim', 'trim'), + new Twig_SimpleFilter('trim', 'twig_trim_filter'), new Twig_SimpleFilter('nl2br', 'nl2br', array('pre_escape' => 'html', 'is_safe' => array('html'))), // array helpers @@ -202,11 +196,6 @@ class Twig_Extension_Core extends Twig_Extension return $filters; } - /** - * Returns a list of global functions to add to the existing list. - * - * @return array An array of global functions - */ public function getFunctions() { return array( @@ -222,22 +211,17 @@ class Twig_Extension_Core extends Twig_Extension ); } - /** - * Returns a list of tests to add to the existing list. - * - * @return array An array of tests - */ public function getTests() { return array( new Twig_SimpleTest('even', null, array('node_class' => 'Twig_Node_Expression_Test_Even')), new Twig_SimpleTest('odd', null, array('node_class' => 'Twig_Node_Expression_Test_Odd')), new Twig_SimpleTest('defined', null, array('node_class' => 'Twig_Node_Expression_Test_Defined')), - new Twig_SimpleTest('sameas', null, array('node_class' => 'Twig_Node_Expression_Test_Sameas', 'deprecated' => true, 'alternative' => 'same as')), + new Twig_SimpleTest('sameas', null, array('node_class' => 'Twig_Node_Expression_Test_Sameas', 'deprecated' => '1.21', 'alternative' => 'same as')), new Twig_SimpleTest('same as', null, array('node_class' => 'Twig_Node_Expression_Test_Sameas')), new Twig_SimpleTest('none', null, array('node_class' => 'Twig_Node_Expression_Test_Null')), new Twig_SimpleTest('null', null, array('node_class' => 'Twig_Node_Expression_Test_Null')), - new Twig_SimpleTest('divisibleby', null, array('node_class' => 'Twig_Node_Expression_Test_Divisibleby', 'deprecated' => true, 'alternative' => 'divisible by')), + new Twig_SimpleTest('divisibleby', null, array('node_class' => 'Twig_Node_Expression_Test_Divisibleby', 'deprecated' => '1.21', 'alternative' => 'divisible by')), new Twig_SimpleTest('divisible by', null, array('node_class' => 'Twig_Node_Expression_Test_Divisibleby')), new Twig_SimpleTest('constant', null, array('node_class' => 'Twig_Node_Expression_Test_Constant')), new Twig_SimpleTest('empty', 'twig_test_empty'), @@ -245,11 +229,6 @@ class Twig_Extension_Core extends Twig_Extension ); } - /** - * Returns a list of operators to add to the existing list. - * - * @return array An array of operators - */ public function getOperators() { return array( @@ -283,85 +262,14 @@ class Twig_Extension_Core extends Twig_Extension '/' => array('precedence' => 60, 'class' => 'Twig_Node_Expression_Binary_Div', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT), '//' => array('precedence' => 60, 'class' => 'Twig_Node_Expression_Binary_FloorDiv', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT), '%' => array('precedence' => 60, 'class' => 'Twig_Node_Expression_Binary_Mod', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT), - 'is' => array('precedence' => 100, 'callable' => array($this, 'parseTestExpression'), 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT), - 'is not' => array('precedence' => 100, 'callable' => array($this, 'parseNotTestExpression'), 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT), + 'is' => array('precedence' => 100, 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT), + 'is not' => array('precedence' => 100, 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT), '**' => array('precedence' => 200, 'class' => 'Twig_Node_Expression_Binary_Power', 'associativity' => Twig_ExpressionParser::OPERATOR_RIGHT), + '??' => array('precedence' => 300, 'class' => 'Twig_Node_Expression_NullCoalesce', 'associativity' => Twig_ExpressionParser::OPERATOR_RIGHT), ), ); } - public function parseNotTestExpression(Twig_Parser $parser, Twig_NodeInterface $node) - { - return new Twig_Node_Expression_Unary_Not($this->parseTestExpression($parser, $node), $parser->getCurrentToken()->getLine()); - } - - public function parseTestExpression(Twig_Parser $parser, Twig_NodeInterface $node) - { - $stream = $parser->getStream(); - list($name, $test) = $this->getTest($parser, $node->getLine()); - - if ($test instanceof Twig_SimpleTest && $test->isDeprecated()) { - $message = sprintf('Twig Test "%s" is deprecated', $name); - if ($test->getAlternative()) { - $message .= sprintf('. Use "%s" instead', $test->getAlternative()); - } - $message .= sprintf(' in %s at line %d.', $stream->getFilename(), $stream->getCurrent()->getLine()); - - @trigger_error($message, E_USER_DEPRECATED); - } - - $class = $this->getTestNodeClass($parser, $test); - $arguments = null; - if ($stream->test(Twig_Token::PUNCTUATION_TYPE, '(')) { - $arguments = $parser->getExpressionParser()->parseArguments(true); - } - - return new $class($node, $name, $arguments, $parser->getCurrentToken()->getLine()); - } - - protected function getTest(Twig_Parser $parser, $line) - { - $stream = $parser->getStream(); - $name = $stream->expect(Twig_Token::NAME_TYPE)->getValue(); - $env = $parser->getEnvironment(); - - if ($test = $env->getTest($name)) { - return array($name, $test); - } - - if ($stream->test(Twig_Token::NAME_TYPE)) { - // try 2-words tests - $name = $name.' '.$parser->getCurrentToken()->getValue(); - - if ($test = $env->getTest($name)) { - $parser->getStream()->next(); - - return array($name, $test); - } - } - - $message = sprintf('The test "%s" does not exist', $name); - if ($alternatives = $env->computeAlternatives($name, array_keys($env->getTests()))) { - $message = sprintf('%s. Did you mean "%s"', $message, implode('", "', $alternatives)); - } - - throw new Twig_Error_Syntax($message, $line, $parser->getFilename()); - } - - protected function getTestNodeClass(Twig_Parser $parser, $test) - { - if ($test instanceof Twig_SimpleTest) { - return $test->getNodeClass(); - } - - return $test instanceof Twig_Test_Node ? $test->getClass() : 'Twig_Node_Expression_Test'; - } - - /** - * Returns the name of the extension. - * - * @return string The extension name - */ public function getName() { return 'core'; @@ -371,7 +279,7 @@ class Twig_Extension_Core extends Twig_Extension /** * Cycles over a value. * - * @param ArrayAccess|array $values An array or an ArrayAccess instance + * @param ArrayAccess|array $values * @param int $position The cycle position * * @return string The next value in the cycle @@ -391,10 +299,10 @@ function twig_cycle($values, $position) * - a random character from a string * - a random integer between 0 and the integer parameter. * - * @param Twig_Environment $env A Twig_Environment instance - * @param Traversable|array|int|string $values The values to pick a random item from + * @param Twig_Environment $env + * @param Traversable|array|int|float|string $values The values to pick a random item from * - * @throws Twig_Error_Runtime When $values is an empty array (does not apply to an empty string which is returned as is). + * @throws Twig_Error_Runtime when $values is an empty array (does not apply to an empty string which is returned as is) * * @return mixed A random value from the given sequence */ @@ -415,7 +323,7 @@ function twig_random(Twig_Environment $env, $values = null) return ''; } if (null !== $charset = $env->getCharset()) { - if ('UTF-8' != $charset) { + if ('UTF-8' !== $charset) { $values = twig_convert_encoding($values, 'UTF-8', $charset); } @@ -423,7 +331,7 @@ function twig_random(Twig_Environment $env, $values = null) // split at all positions, but not after the start and not before the end $values = preg_split('/(? $value) { $values[$i] = twig_convert_encoding($value, $charset, 'UTF-8'); } @@ -451,7 +359,7 @@ function twig_random(Twig_Environment $env, $values = null) * {{ post.published_at|date("m/d/Y") }} * * - * @param Twig_Environment $env A Twig_Environment instance + * @param Twig_Environment $env * @param DateTime|DateTimeInterface|DateInterval|string $date A date * @param string|null $format The target format, null to use the default * @param DateTimeZone|string|null|false $timezone The target timezone, null to use the default, false to leave unchanged @@ -461,7 +369,7 @@ function twig_random(Twig_Environment $env, $values = null) function twig_date_format_filter(Twig_Environment $env, $date, $format = null, $timezone = null) { if (null === $format) { - $formats = $env->getExtension('core')->getDateFormat(); + $formats = $env->getExtension('Twig_Extension_Core')->getDateFormat(); $format = $date instanceof DateInterval ? $formats[1] : $formats[0]; } @@ -479,7 +387,7 @@ function twig_date_format_filter(Twig_Environment $env, $date, $format = null, $ * {{ post.published_at|date_modify("-1day")|date("m/d/Y") }} * * - * @param Twig_Environment $env A Twig_Environment instance + * @param Twig_Environment $env * @param DateTime|string $date A date * @param string $modifier A modifier string * @@ -505,7 +413,7 @@ function twig_date_modify_filter(Twig_Environment $env, $date, $modifier) * {% endif %} * * - * @param Twig_Environment $env A Twig_Environment instance + * @param Twig_Environment $env * @param DateTime|DateTimeInterface|string|null $date A date * @param DateTimeZone|string|null|false $timezone The target timezone, null to use the default, false to leave unchanged * @@ -516,7 +424,7 @@ function twig_date_converter(Twig_Environment $env, $date = null, $timezone = nu // determine the timezone if (false !== $timezone) { if (null === $timezone) { - $timezone = $env->getExtension('core')->getTimezone(); + $timezone = $env->getExtension('Twig_Extension_Core')->getTimezone(); } elseif (!$timezone instanceof DateTimeZone) { $timezone = new DateTimeZone($timezone); } @@ -536,12 +444,17 @@ function twig_date_converter(Twig_Environment $env, $date = null, $timezone = nu return $date; } - $asString = (string) $date; - if (ctype_digit($asString) || (!empty($asString) && '-' === $asString[0] && ctype_digit(substr($asString, 1)))) { - $date = '@'.$date; + if (null === $date || 'now' === $date) { + return new DateTime($date, false !== $timezone ? $timezone : $env->getExtension('Twig_Extension_Core')->getTimezone()); + } + + $asString = (string) $date; + if (ctype_digit($asString) || (!empty($asString) && '-' === $asString[0] && ctype_digit(substr($asString, 1)))) { + $date = new DateTime('@'.$date); + } else { + $date = new DateTime($date, $env->getExtension('Twig_Extension_Core')->getTimezone()); } - $date = new DateTime($date, $env->getExtension('core')->getTimezone()); if (false !== $timezone) { $date->setTimezone($timezone); } @@ -563,11 +476,11 @@ function twig_replace_filter($str, $from, $to = null) if ($from instanceof Traversable) { $from = iterator_to_array($from); } elseif (is_string($from) && is_string($to)) { - @trigger_error('Using "replace" with character by character replacement is deprecated and will be removed in Twig 2.0', E_USER_DEPRECATED); + @trigger_error('Using "replace" with character by character replacement is deprecated since version 1.22 and will be removed in Twig 2.0', E_USER_DEPRECATED); return strtr($str, $from, $to); } elseif (!is_array($from)) { - throw new Twig_Error_Runtime(sprintf('The "replace" filter expects an array or "Traversable" as replace values, got "%s".',is_object($from) ? get_class($from) : gettype($from))); + throw new Twig_Error_Runtime(sprintf('The "replace" filter expects an array or "Traversable" as replace values, got "%s".', is_object($from) ? get_class($from) : gettype($from))); } return strtr($str, $from); @@ -602,17 +515,17 @@ function twig_round($value, $precision = 0, $method = 'common') * be used. Supplying any of the parameters will override the defaults set in the * environment object. * - * @param Twig_Environment $env A Twig_Environment instance + * @param Twig_Environment $env * @param mixed $number A float/int/string of the number to format - * @param int $decimal The number of decimal points to display. - * @param string $decimalPoint The character(s) to use for the decimal point. - * @param string $thousandSep The character(s) to use for the thousands separator. + * @param int $decimal the number of decimal points to display + * @param string $decimalPoint the character(s) to use for the decimal point + * @param string $thousandSep the character(s) to use for the thousands separator * * @return string The formatted number */ function twig_number_format_filter(Twig_Environment $env, $number, $decimal = null, $decimalPoint = null, $thousandSep = null) { - $defaults = $env->getExtension('core')->getNumberFormat(); + $defaults = $env->getExtension('Twig_Extension_Core')->getNumberFormat(); if (null === $decimal) { $decimal = $defaults[0]; } @@ -652,7 +565,7 @@ if (PHP_VERSION_ID < 50300) { /** * JSON encodes a variable. * - * @param mixed $value The value to encode. + * @param mixed $value the value to encode * @param int $options Not used on PHP 5.2.x * * @return mixed The JSON encoded value @@ -671,7 +584,7 @@ if (PHP_VERSION_ID < 50300) { /** * JSON encodes a variable. * - * @param mixed $value The value to encode. + * @param mixed $value the value to encode * @param int $options Bitmask consisting of JSON_HEX_QUOT, JSON_HEX_TAG, JSON_HEX_AMP, JSON_HEX_APOS, JSON_NUMERIC_CHECK, JSON_PRETTY_PRINT, JSON_UNESCAPED_SLASHES, JSON_FORCE_OBJECT * * @return mixed The JSON encoded value @@ -731,7 +644,7 @@ function twig_array_merge($arr1, $arr2) /** * Slices a variable. * - * @param Twig_Environment $env A Twig_Environment instance + * @param Twig_Environment $env * @param mixed $item A variable * @param int $start Start of the slice * @param int $length Size of the slice @@ -742,7 +655,7 @@ function twig_array_merge($arr1, $arr2) function twig_slice(Twig_Environment $env, $item, $start, $length = null, $preserveKeys = false) { if ($item instanceof Traversable) { - if ($item instanceof IteratorAggregate) { + while ($item instanceof IteratorAggregate) { $item = $item->getIterator(); } @@ -773,7 +686,7 @@ function twig_slice(Twig_Environment $env, $item, $start, $length = null, $prese /** * Returns the first element of the item. * - * @param Twig_Environment $env A Twig_Environment instance + * @param Twig_Environment $env * @param mixed $item A variable * * @return mixed The first element of the item @@ -788,7 +701,7 @@ function twig_first(Twig_Environment $env, $item) /** * Returns the last element of the item. * - * @param Twig_Environment $env A Twig_Environment instance + * @param Twig_Environment $env * @param mixed $item A variable * * @return mixed The last element of the item @@ -844,9 +757,10 @@ function twig_join_filter($value, $glue = '') * {# returns [aa, bb, cc] #} * * - * @param string $value A string - * @param string $delimiter The delimiter - * @param int $limit The limit + * @param Twig_Environment $env + * @param string $value A string + * @param string $delimiter The delimiter + * @param int $limit The limit * * @return array The split string as an array */ @@ -880,6 +794,9 @@ function twig_split_filter(Twig_Environment $env, $value, $delimiter, $limit = n // The '_default' filter is used internally to avoid using the ternary operator // which costs a lot for big contexts (before PHP 5.4). So, on average, // a function call is cheaper. +/** + * @internal + */ function _twig_default_filter($value, $default = '') { if (twig_test_empty($value)) { @@ -907,7 +824,27 @@ function _twig_default_filter($value, $default = '') function twig_get_array_keys_filter($array) { if ($array instanceof Traversable) { - return array_keys(iterator_to_array($array)); + while ($array instanceof IteratorAggregate) { + $array = $array->getIterator(); + } + + if ($array instanceof Iterator) { + $keys = array(); + $array->rewind(); + while ($array->valid()) { + $keys[] = $array->key(); + $array->next(); + } + + return $keys; + } + + $keys = array(); + foreach ($array as $key => $item) { + $keys[] = $key; + } + + return $keys; } if (!is_array($array)) { @@ -920,7 +857,7 @@ function twig_get_array_keys_filter($array) /** * Reverses a variable. * - * @param Twig_Environment $env A Twig_Environment instance + * @param Twig_Environment $env * @param array|Traversable|string $item An array, a Traversable instance, or a string * @param bool $preserveKeys Whether to preserve key or not * @@ -939,7 +876,7 @@ function twig_reverse_filter(Twig_Environment $env, $item, $preserveKeys = false if (null !== $charset = $env->getCharset()) { $string = (string) $item; - if ('UTF-8' != $charset) { + if ('UTF-8' !== $charset) { $item = twig_convert_encoding($string, 'UTF-8', $charset); } @@ -947,7 +884,7 @@ function twig_reverse_filter(Twig_Environment $env, $item, $preserveKeys = false $string = implode('', array_reverse($matches[0])); - if ('UTF-8' != $charset) { + if ('UTF-8' !== $charset) { $string = twig_convert_encoding($string, $charset, 'UTF-8'); } @@ -977,7 +914,9 @@ function twig_sort_filter($array) return $array; } -/* used internally */ +/** + * @internal + */ function twig_in_filter($value, $compare) { if (is_array($compare)) { @@ -985,17 +924,56 @@ function twig_in_filter($value, $compare) } elseif (is_string($compare) && (is_string($value) || is_int($value) || is_float($value))) { return '' === $value || false !== strpos($compare, (string) $value); } elseif ($compare instanceof Traversable) { - return in_array($value, iterator_to_array($compare, false), is_object($value) || is_resource($value)); + if (is_object($value) || is_resource($value)) { + foreach ($compare as $item) { + if ($item === $value) { + return true; + } + } + } else { + foreach ($compare as $item) { + if ($item == $value) { + return true; + } + } + } + + return false; } return false; } +/** + * Returns a trimmed string. + * + * @return string + * + * @throws Twig_Error_Runtime When an invalid trimming side is used (not a string or not 'left', 'right', or 'both') + */ +function twig_trim_filter($string, $characterMask = null, $side = 'both') +{ + if (null === $characterMask) { + $characterMask = " \t\n\r\0\x0B"; + } + + switch ($side) { + case 'both': + return trim($string, $characterMask); + case 'left': + return ltrim($string, $characterMask); + case 'right': + return rtrim($string, $characterMask); + default: + throw new Twig_Error_Runtime('Trimming side must be "left", "right" or "both".'); + } +} + /** * Escapes a string. * - * @param Twig_Environment $env A Twig_Environment instance - * @param string $string The value to be escaped + * @param Twig_Environment $env + * @param mixed $string The value to be escaped * @param string $strategy The escaping strategy * @param string $charset The charset * @param bool $autoescape Whether the function is called by the auto-escaping feature (true) or by the developer (false) @@ -1011,7 +989,7 @@ function twig_escape_filter(Twig_Environment $env, $string, $strategy = 'html', if (!is_string($string)) { if (is_object($string) && method_exists($string, '__toString')) { $string = (string) $string; - } else { + } elseif (in_array($strategy, array('html', 'js', 'css', 'html_attr', 'url'))) { return $string; } } @@ -1027,30 +1005,22 @@ function twig_escape_filter(Twig_Environment $env, $string, $strategy = 'html', // Using a static variable to avoid initializing the array // each time the function is called. Moving the declaration on the // top of the function slow downs other escaping strategies. - static $htmlspecialcharsCharsets; - - if (null === $htmlspecialcharsCharsets) { - if (defined('HHVM_VERSION')) { - $htmlspecialcharsCharsets = array('utf-8' => true, 'UTF-8' => true); - } else { - $htmlspecialcharsCharsets = array( - 'ISO-8859-1' => true, 'ISO8859-1' => true, - 'ISO-8859-15' => true, 'ISO8859-15' => true, - 'utf-8' => true, 'UTF-8' => true, - 'CP866' => true, 'IBM866' => true, '866' => true, - 'CP1251' => true, 'WINDOWS-1251' => true, 'WIN-1251' => true, - '1251' => true, - 'CP1252' => true, 'WINDOWS-1252' => true, '1252' => true, - 'KOI8-R' => true, 'KOI8-RU' => true, 'KOI8R' => true, - 'BIG5' => true, '950' => true, - 'GB2312' => true, '936' => true, - 'BIG5-HKSCS' => true, - 'SHIFT_JIS' => true, 'SJIS' => true, '932' => true, - 'EUC-JP' => true, 'EUCJP' => true, - 'ISO8859-5' => true, 'ISO-8859-5' => true, 'MACROMAN' => true, - ); - } - } + static $htmlspecialcharsCharsets = array( + 'ISO-8859-1' => true, 'ISO8859-1' => true, + 'ISO-8859-15' => true, 'ISO8859-15' => true, + 'utf-8' => true, 'UTF-8' => true, + 'CP866' => true, 'IBM866' => true, '866' => true, + 'CP1251' => true, 'WINDOWS-1251' => true, 'WIN-1251' => true, + '1251' => true, + 'CP1252' => true, 'WINDOWS-1252' => true, '1252' => true, + 'KOI8-R' => true, 'KOI8-RU' => true, 'KOI8R' => true, + 'BIG5' => true, '950' => true, + 'GB2312' => true, '936' => true, + 'BIG5-HKSCS' => true, + 'SHIFT_JIS' => true, 'SJIS' => true, '932' => true, + 'EUC-JP' => true, 'EUCJP' => true, + 'ISO8859-5' => true, 'ISO-8859-5' => true, 'MACROMAN' => true, + ); if (isset($htmlspecialcharsCharsets[$charset])) { return htmlspecialchars($string, ENT_QUOTES | ENT_SUBSTITUTE, $charset); @@ -1071,51 +1041,51 @@ function twig_escape_filter(Twig_Environment $env, $string, $strategy = 'html', case 'js': // escape all non-alphanumeric characters // into their \xHH or \uHHHH representations - if ('UTF-8' != $charset) { + if ('UTF-8' !== $charset) { $string = twig_convert_encoding($string, 'UTF-8', $charset); } - if (0 == strlen($string) ? false : (1 == preg_match('/^./su', $string) ? false : true)) { + if (0 == strlen($string) ? false : 1 !== preg_match('/^./su', $string)) { throw new Twig_Error_Runtime('The string to escape is not a valid UTF-8 string.'); } $string = preg_replace_callback('#[^a-zA-Z0-9,\._]#Su', '_twig_escape_js_callback', $string); - if ('UTF-8' != $charset) { + if ('UTF-8' !== $charset) { $string = twig_convert_encoding($string, $charset, 'UTF-8'); } return $string; case 'css': - if ('UTF-8' != $charset) { + if ('UTF-8' !== $charset) { $string = twig_convert_encoding($string, 'UTF-8', $charset); } - if (0 == strlen($string) ? false : (1 == preg_match('/^./su', $string) ? false : true)) { + if (0 == strlen($string) ? false : 1 !== preg_match('/^./su', $string)) { throw new Twig_Error_Runtime('The string to escape is not a valid UTF-8 string.'); } $string = preg_replace_callback('#[^a-zA-Z0-9]#Su', '_twig_escape_css_callback', $string); - if ('UTF-8' != $charset) { + if ('UTF-8' !== $charset) { $string = twig_convert_encoding($string, $charset, 'UTF-8'); } return $string; case 'html_attr': - if ('UTF-8' != $charset) { + if ('UTF-8' !== $charset) { $string = twig_convert_encoding($string, 'UTF-8', $charset); } - if (0 == strlen($string) ? false : (1 == preg_match('/^./su', $string) ? false : true)) { + if (0 == strlen($string) ? false : 1 !== preg_match('/^./su', $string)) { throw new Twig_Error_Runtime('The string to escape is not a valid UTF-8 string.'); } $string = preg_replace_callback('#[^a-zA-Z0-9,\.\-_]#Su', '_twig_escape_html_attr_callback', $string); - if ('UTF-8' != $charset) { + if ('UTF-8' !== $charset) { $string = twig_convert_encoding($string, $charset, 'UTF-8'); } @@ -1132,7 +1102,7 @@ function twig_escape_filter(Twig_Environment $env, $string, $strategy = 'html', static $escapers; if (null === $escapers) { - $escapers = $env->getExtension('core')->getEscapers(); + $escapers = $env->getExtension('Twig_Extension_Core')->getEscapers(); } if (isset($escapers[$strategy])) { @@ -1145,7 +1115,9 @@ function twig_escape_filter(Twig_Environment $env, $string, $strategy = 'html', } } -/* used internally */ +/** + * @internal + */ function twig_escape_filter_is_safe(Twig_Node $filterArgs) { foreach ($filterArgs as $arg) { @@ -1187,8 +1159,13 @@ function _twig_escape_js_callback($matches) // \uHHHH $char = twig_convert_encoding($char, 'UTF-16BE', 'UTF-8'); + $char = strtoupper(bin2hex($char)); - return '\\u'.strtoupper(substr('0000'.bin2hex($char), -4)); + if (4 >= strlen($char)) { + return sprintf('\u%04s', $char); + } + + return sprintf('\u%04s\u%04s', substr($char, 0, -4), substr($char, -4)); } function _twig_escape_css_callback($matches) @@ -1271,27 +1248,43 @@ if (function_exists('mb_get_info')) { /** * Returns the length of a variable. * - * @param Twig_Environment $env A Twig_Environment instance + * @param Twig_Environment $env * @param mixed $thing A variable * * @return int The length of the value */ function twig_length_filter(Twig_Environment $env, $thing) { - return is_scalar($thing) ? mb_strlen($thing, $env->getCharset()) : count($thing); + if (null === $thing) { + return 0; + } + + if (is_scalar($thing)) { + return mb_strlen($thing, $env->getCharset()); + } + + if (is_object($thing) && method_exists($thing, '__toString') && !$thing instanceof \Countable) { + return mb_strlen((string) $thing, $env->getCharset()); + } + + if ($thing instanceof \Countable || is_array($thing)) { + return count($thing); + } + + return 1; } /** * Converts a string to uppercase. * - * @param Twig_Environment $env A Twig_Environment instance + * @param Twig_Environment $env * @param string $string A string * * @return string The uppercased string */ function twig_upper_filter(Twig_Environment $env, $string) { - if (null !== ($charset = $env->getCharset())) { + if (null !== $charset = $env->getCharset()) { return mb_strtoupper($string, $charset); } @@ -1301,14 +1294,14 @@ if (function_exists('mb_get_info')) { /** * Converts a string to lowercase. * - * @param Twig_Environment $env A Twig_Environment instance + * @param Twig_Environment $env * @param string $string A string * * @return string The lowercased string */ function twig_lower_filter(Twig_Environment $env, $string) { - if (null !== ($charset = $env->getCharset())) { + if (null !== $charset = $env->getCharset()) { return mb_strtolower($string, $charset); } @@ -1318,14 +1311,14 @@ if (function_exists('mb_get_info')) { /** * Returns a titlecased string. * - * @param Twig_Environment $env A Twig_Environment instance + * @param Twig_Environment $env * @param string $string A string * * @return string The titlecased string */ function twig_title_string_filter(Twig_Environment $env, $string) { - if (null !== ($charset = $env->getCharset())) { + if (null !== $charset = $env->getCharset()) { return mb_convert_case($string, MB_CASE_TITLE, $charset); } @@ -1335,7 +1328,7 @@ if (function_exists('mb_get_info')) { /** * Returns a capitalized string. * - * @param Twig_Environment $env A Twig_Environment instance + * @param Twig_Environment $env * @param string $string A string * * @return string The capitalized string @@ -1354,20 +1347,36 @@ else { /** * Returns the length of a variable. * - * @param Twig_Environment $env A Twig_Environment instance + * @param Twig_Environment $env * @param mixed $thing A variable * * @return int The length of the value */ function twig_length_filter(Twig_Environment $env, $thing) { - return is_scalar($thing) ? strlen($thing) : count($thing); + if (null === $thing) { + return 0; + } + + if (is_scalar($thing)) { + return strlen($thing); + } + + if (is_object($thing) && method_exists($thing, '__toString') && !$thing instanceof \Countable) { + return strlen((string) $thing); + } + + if ($thing instanceof \Countable || is_array($thing)) { + return count($thing); + } + + return 1; } /** * Returns a titlecased string. * - * @param Twig_Environment $env A Twig_Environment instance + * @param Twig_Environment $env * @param string $string A string * * @return string The titlecased string @@ -1380,7 +1389,7 @@ else { /** * Returns a capitalized string. * - * @param Twig_Environment $env A Twig_Environment instance + * @param Twig_Environment $env * @param string $string A string * * @return string The capitalized string @@ -1391,7 +1400,9 @@ else { } } -/* used internally */ +/** + * @internal + */ function twig_ensure_traversable($seq) { if ($seq instanceof Traversable || is_array($seq)) { @@ -1421,6 +1432,10 @@ function twig_test_empty($value) return 0 == count($value); } + if (is_object($value) && method_exists($value, '__toString')) { + return '' === (string) $value; + } + return '' === $value || false === $value || null === $value || array() === $value; } @@ -1464,8 +1479,8 @@ function twig_include(Twig_Environment $env, $context, $template, $variables = a $variables = array_merge($context, $variables); } - if ($isSandboxed = $sandboxed && $env->hasExtension('sandbox')) { - $sandbox = $env->getExtension('sandbox'); + if ($isSandboxed = $sandboxed && $env->hasExtension('Twig_Extension_Sandbox')) { + $sandbox = $env->getExtension('Twig_Extension_Sandbox'); if (!$alreadySandboxed = $sandbox->isSandboxed()) { $sandbox->enableSandbox(); } @@ -1482,6 +1497,18 @@ function twig_include(Twig_Environment $env, $context, $template, $variables = a throw $e; } + } catch (Throwable $e) { + if ($isSandboxed && !$alreadySandboxed) { + $sandbox->disableSandbox(); + } + + throw $e; + } catch (Exception $e) { + if ($isSandboxed && !$alreadySandboxed) { + $sandbox->disableSandbox(); + } + + throw $e; } if ($isSandboxed && !$alreadySandboxed) { @@ -1494,15 +1521,21 @@ function twig_include(Twig_Environment $env, $context, $template, $variables = a /** * Returns a template content without rendering it. * - * @param string $name The template name - * @param bool $ignoreMissing Whether to ignore missing templates or not + * @param Twig_Environment $env + * @param string $name The template name + * @param bool $ignoreMissing Whether to ignore missing templates or not * * @return string The template source */ function twig_source(Twig_Environment $env, $name, $ignoreMissing = false) { + $loader = $env->getLoader(); try { - return $env->getLoader()->getSource($name); + if (!$loader instanceof Twig_SourceContextLoaderInterface) { + return $loader->getSource($name); + } else { + return $loader->getSourceContext($name)->getCode(); + } } catch (Twig_Error_Loader $e) { if (!$ignoreMissing) { throw $e; @@ -1527,6 +1560,23 @@ function twig_constant($constant, $object = null) return constant($constant); } +/** + * Checks if a constant exists. + * + * @param string $constant The name of the constant + * @param null|object $object The object to get the constant from + * + * @return bool + */ +function twig_constant_is_defined($constant, $object = null) +{ + if (null !== $object) { + $constant = get_class($object).'::'.$constant; + } + + return defined($constant); +} + /** * Batches item. * @@ -1558,3 +1608,5 @@ function twig_array_batch($items, $size, $fill = null) return $result; } + +class_alias('Twig_Extension_Core', 'Twig\Extension\CoreExtension', false); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/Extension/Debug.php b/lib/silex/vendor/twig/twig/lib/Twig/Extension/Debug.php index 86d07c288..d0cd1962b 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/Extension/Debug.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/Extension/Debug.php @@ -3,18 +3,17 @@ /* * This file is part of Twig. * - * (c) 2011 Fabien Potencier + * (c) Fabien Potencier * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ + +/** + * @final + */ class Twig_Extension_Debug extends Twig_Extension { - /** - * Returns a list of global functions to add to the existing list. - * - * @return array An array of global functions - */ public function getFunctions() { // dump is safe if var_dump is overridden by xdebug @@ -24,7 +23,7 @@ class Twig_Extension_Debug extends Twig_Extension // false means that it was not set (and the default is on) or it explicitly enabled // xdebug.overload_var_dump produces HTML only when html_errors is also enabled && (false === ini_get('html_errors') || ini_get('html_errors')) - || 'cli' === php_sapi_name() + || 'cli' === PHP_SAPI ; return array( @@ -32,11 +31,6 @@ class Twig_Extension_Debug extends Twig_Extension ); } - /** - * Returns the name of the extension. - * - * @return string The extension name - */ public function getName() { return 'debug'; @@ -69,3 +63,5 @@ function twig_var_dump(Twig_Environment $env, $context) return ob_get_clean(); } + +class_alias('Twig_Extension_Debug', 'Twig\Extension\DebugExtension', false); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/Extension/Escaper.php b/lib/silex/vendor/twig/twig/lib/Twig/Extension/Escaper.php index 053a895c2..46c2d84b6 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/Extension/Escaper.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/Extension/Escaper.php @@ -3,18 +3,20 @@ /* * This file is part of Twig. * - * (c) 2009 Fabien Potencier + * (c) Fabien Potencier * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ + +/** + * @final + */ class Twig_Extension_Escaper extends Twig_Extension { protected $defaultStrategy; /** - * Constructor. - * * @param string|false|callable $defaultStrategy An escaping strategy * * @see setDefaultStrategy() @@ -24,31 +26,16 @@ class Twig_Extension_Escaper extends Twig_Extension $this->setDefaultStrategy($defaultStrategy); } - /** - * Returns the token parser instances to add to the existing list. - * - * @return array An array of Twig_TokenParserInterface or Twig_TokenParserBrokerInterface instances - */ public function getTokenParsers() { return array(new Twig_TokenParser_AutoEscape()); } - /** - * Returns the node visitor instances to add to the existing list. - * - * @return Twig_NodeVisitorInterface[] An array of Twig_NodeVisitorInterface instances - */ public function getNodeVisitors() { return array(new Twig_NodeVisitor_Escaper()); } - /** - * Returns a list of filters to add to the existing list. - * - * @return array An array of filters - */ public function getFilters() { return array( @@ -60,7 +47,7 @@ class Twig_Extension_Escaper extends Twig_Extension * Sets the default strategy to use when not defined by the user. * * The strategy can be a valid PHP callback that takes the template - * "filename" as an argument and returns the strategy to use. + * name as an argument and returns the strategy to use. * * @param string|false|callable $defaultStrategy An escaping strategy */ @@ -68,12 +55,18 @@ class Twig_Extension_Escaper extends Twig_Extension { // for BC if (true === $defaultStrategy) { - @trigger_error('Using "true" as the default strategy is deprecated. Use "html" instead.', E_USER_DEPRECATED); + @trigger_error('Using "true" as the default strategy is deprecated since version 1.21. Use "html" instead.', E_USER_DEPRECATED); $defaultStrategy = 'html'; } if ('filename' === $defaultStrategy) { + @trigger_error('Using "filename" as the default strategy is deprecated since version 1.27. Use "name" instead.', E_USER_DEPRECATED); + + $defaultStrategy = 'name'; + } + + if ('name' === $defaultStrategy) { $defaultStrategy = array('Twig_FileExtensionEscapingStrategy', 'guess'); } @@ -83,26 +76,21 @@ class Twig_Extension_Escaper extends Twig_Extension /** * Gets the default strategy to use when not defined by the user. * - * @param string $filename The template "filename" + * @param string $name The template name * * @return string|false The default strategy to use for the template */ - public function getDefaultStrategy($filename) + public function getDefaultStrategy($name) { // disable string callables to avoid calling a function named html or js, // or any other upcoming escaping strategy if (!is_string($this->defaultStrategy) && false !== $this->defaultStrategy) { - return call_user_func($this->defaultStrategy, $filename); + return call_user_func($this->defaultStrategy, $name); } return $this->defaultStrategy; } - /** - * Returns the name of the extension. - * - * @return string The extension name - */ public function getName() { return 'escaper'; @@ -120,3 +108,5 @@ function twig_raw_filter($string) { return $string; } + +class_alias('Twig_Extension_Escaper', 'Twig\Extension\EscaperExtension', false); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/Extension/GlobalsInterface.php b/lib/silex/vendor/twig/twig/lib/Twig/Extension/GlobalsInterface.php new file mode 100644 index 000000000..922cd2c93 --- /dev/null +++ b/lib/silex/vendor/twig/twig/lib/Twig/Extension/GlobalsInterface.php @@ -0,0 +1,24 @@ + + */ +interface Twig_Extension_GlobalsInterface +{ +} + +class_alias('Twig_Extension_GlobalsInterface', 'Twig\Extension\GlobalsInterface', false); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/Extension/InitRuntimeInterface.php b/lib/silex/vendor/twig/twig/lib/Twig/Extension/InitRuntimeInterface.php new file mode 100644 index 000000000..1549862f4 --- /dev/null +++ b/lib/silex/vendor/twig/twig/lib/Twig/Extension/InitRuntimeInterface.php @@ -0,0 +1,24 @@ + + */ +interface Twig_Extension_InitRuntimeInterface +{ +} + +class_alias('Twig_Extension_InitRuntimeInterface', 'Twig\Extension\InitRuntimeInterface', false); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/Extension/Optimizer.php b/lib/silex/vendor/twig/twig/lib/Twig/Extension/Optimizer.php index 013fcb625..6c62e3efc 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/Extension/Optimizer.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/Extension/Optimizer.php @@ -3,11 +3,15 @@ /* * This file is part of Twig. * - * (c) 2010 Fabien Potencier + * (c) Fabien Potencier * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ + +/** + * @final + */ class Twig_Extension_Optimizer extends Twig_Extension { protected $optimizers; @@ -17,19 +21,15 @@ class Twig_Extension_Optimizer extends Twig_Extension $this->optimizers = $optimizers; } - /** - * {@inheritdoc} - */ public function getNodeVisitors() { return array(new Twig_NodeVisitor_Optimizer($this->optimizers)); } - /** - * {@inheritdoc} - */ public function getName() { return 'optimizer'; } } + +class_alias('Twig_Extension_Optimizer', 'Twig\Extension\OptimizerExtension', false); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/Extension/Profiler.php b/lib/silex/vendor/twig/twig/lib/Twig/Extension/Profiler.php index e21fdb6e2..fcfc002ba 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/Extension/Profiler.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/Extension/Profiler.php @@ -3,7 +3,7 @@ /* * This file is part of Twig. * - * (c) 2015 Fabien Potencier + * (c) Fabien Potencier * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -34,19 +34,16 @@ class Twig_Extension_Profiler extends Twig_Extension } } - /** - * {@inheritdoc} - */ public function getNodeVisitors() { - return array(new Twig_Profiler_NodeVisitor_Profiler($this->getName())); + return array(new Twig_Profiler_NodeVisitor_Profiler(get_class($this))); } - /** - * {@inheritdoc} - */ public function getName() { return 'profiler'; } } + +class_alias('Twig_Extension_Profiler', 'Twig\Extension\ProfilerExtension', false); +class_exists('Twig_Profiler_Profile'); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/Extension/Sandbox.php b/lib/silex/vendor/twig/twig/lib/Twig/Extension/Sandbox.php index 3593e9eb4..5cb80a717 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/Extension/Sandbox.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/Extension/Sandbox.php @@ -3,11 +3,15 @@ /* * This file is part of Twig. * - * (c) 2009 Fabien Potencier + * (c) Fabien Potencier * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ + +/** + * @final + */ class Twig_Extension_Sandbox extends Twig_Extension { protected $sandboxedGlobally; @@ -20,21 +24,11 @@ class Twig_Extension_Sandbox extends Twig_Extension $this->sandboxedGlobally = $sandboxed; } - /** - * Returns the token parser instances to add to the existing list. - * - * @return array An array of Twig_TokenParserInterface or Twig_TokenParserBrokerInterface instances - */ public function getTokenParsers() { return array(new Twig_TokenParser_Sandbox()); } - /** - * Returns the node visitor instances to add to the existing list. - * - * @return Twig_NodeVisitorInterface[] An array of Twig_NodeVisitorInterface instances - */ public function getNodeVisitors() { return array(new Twig_NodeVisitor_Sandbox()); @@ -100,13 +94,10 @@ class Twig_Extension_Sandbox extends Twig_Extension return $obj; } - /** - * Returns the name of the extension. - * - * @return string The extension name - */ public function getName() { return 'sandbox'; } } + +class_alias('Twig_Extension_Sandbox', 'Twig\Extension\SandboxExtension', false); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/Extension/Staging.php b/lib/silex/vendor/twig/twig/lib/Twig/Extension/Staging.php index 8ab0f4596..d3a0f9c94 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/Extension/Staging.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/Extension/Staging.php @@ -3,7 +3,7 @@ /* * This file is part of Twig. * - * (c) 2012 Fabien Potencier + * (c) Fabien Potencier * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -15,6 +15,8 @@ * This class is used by Twig_Environment as a staging area and must not be used directly. * * @author Fabien Potencier + * + * @internal */ class Twig_Extension_Staging extends Twig_Extension { @@ -27,12 +29,13 @@ class Twig_Extension_Staging extends Twig_Extension public function addFunction($name, $function) { + if (isset($this->functions[$name])) { + @trigger_error(sprintf('Overriding function "%s" that is already registered is deprecated since version 1.30 and won\'t be possible anymore in 2.0.', $name), E_USER_DEPRECATED); + } + $this->functions[$name] = $function; } - /** - * {@inheritdoc} - */ public function getFunctions() { return $this->functions; @@ -40,12 +43,13 @@ class Twig_Extension_Staging extends Twig_Extension public function addFilter($name, $filter) { + if (isset($this->filters[$name])) { + @trigger_error(sprintf('Overriding filter "%s" that is already registered is deprecated since version 1.30 and won\'t be possible anymore in 2.0.', $name), E_USER_DEPRECATED); + } + $this->filters[$name] = $filter; } - /** - * {@inheritdoc} - */ public function getFilters() { return $this->filters; @@ -56,9 +60,6 @@ class Twig_Extension_Staging extends Twig_Extension $this->visitors[] = $visitor; } - /** - * {@inheritdoc} - */ public function getNodeVisitors() { return $this->visitors; @@ -66,12 +67,13 @@ class Twig_Extension_Staging extends Twig_Extension public function addTokenParser(Twig_TokenParserInterface $parser) { - $this->tokenParsers[] = $parser; + if (isset($this->tokenParsers[$parser->getTag()])) { + @trigger_error(sprintf('Overriding tag "%s" that is already registered is deprecated since version 1.30 and won\'t be possible anymore in 2.0.', $parser->getTag()), E_USER_DEPRECATED); + } + + $this->tokenParsers[$parser->getTag()] = $parser; } - /** - * {@inheritdoc} - */ public function getTokenParsers() { return $this->tokenParsers; @@ -82,9 +84,6 @@ class Twig_Extension_Staging extends Twig_Extension $this->globals[$name] = $value; } - /** - * {@inheritdoc} - */ public function getGlobals() { return $this->globals; @@ -92,22 +91,22 @@ class Twig_Extension_Staging extends Twig_Extension public function addTest($name, $test) { + if (isset($this->tests[$name])) { + @trigger_error(sprintf('Overriding test "%s" that is already registered is deprecated since version 1.30 and won\'t be possible anymore in 2.0.', $name), E_USER_DEPRECATED); + } + $this->tests[$name] = $test; } - /** - * {@inheritdoc} - */ public function getTests() { return $this->tests; } - /** - * {@inheritdoc} - */ public function getName() { return 'staging'; } } + +class_alias('Twig_Extension_Staging', 'Twig\Extension\StagingExtension', false); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/Extension/StringLoader.php b/lib/silex/vendor/twig/twig/lib/Twig/Extension/StringLoader.php index 4e1a546c2..2ce3c9924 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/Extension/StringLoader.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/Extension/StringLoader.php @@ -3,16 +3,17 @@ /* * This file is part of Twig. * - * (c) 2012 Fabien Potencier + * (c) Fabien Potencier * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ + +/** + * @final + */ class Twig_Extension_StringLoader extends Twig_Extension { - /** - * {@inheritdoc} - */ public function getFunctions() { return array( @@ -20,9 +21,6 @@ class Twig_Extension_StringLoader extends Twig_Extension ); } - /** - * {@inheritdoc} - */ public function getName() { return 'string_loader'; @@ -37,11 +35,13 @@ class Twig_Extension_StringLoader extends Twig_Extension * * * @param Twig_Environment $env A Twig_Environment instance - * @param string $template A template as a string + * @param string $template A template as a string or object implementing __toString() * - * @return Twig_Template A Twig_Template instance + * @return Twig_Template */ function twig_template_from_string(Twig_Environment $env, $template) { - return $env->createTemplate($template); + return $env->createTemplate((string) $template); } + +class_alias('Twig_Extension_StringLoader', 'Twig\Extension\StringLoaderExtension', false); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/ExtensionInterface.php b/lib/silex/vendor/twig/twig/lib/Twig/ExtensionInterface.php index 49541b02e..946df500a 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/ExtensionInterface.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/ExtensionInterface.php @@ -3,7 +3,7 @@ /* * This file is part of Twig. * - * (c) 2009 Fabien Potencier + * (c) Fabien Potencier * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -21,49 +21,49 @@ interface Twig_ExtensionInterface * * This is where you can load some file that contains filter functions for instance. * - * @param Twig_Environment $environment The current Twig_Environment instance + * @deprecated since 1.23 (to be removed in 2.0), implement Twig_Extension_InitRuntimeInterface instead */ public function initRuntime(Twig_Environment $environment); /** * Returns the token parser instances to add to the existing list. * - * @return array An array of Twig_TokenParserInterface or Twig_TokenParserBrokerInterface instances + * @return Twig_TokenParserInterface[] */ public function getTokenParsers(); /** * Returns the node visitor instances to add to the existing list. * - * @return Twig_NodeVisitorInterface[] An array of Twig_NodeVisitorInterface instances + * @return Twig_NodeVisitorInterface[] */ public function getNodeVisitors(); /** * Returns a list of filters to add to the existing list. * - * @return array An array of filters + * @return Twig_SimpleFilter[] */ public function getFilters(); /** * Returns a list of tests to add to the existing list. * - * @return array An array of tests + * @return Twig_SimpleTest[] */ public function getTests(); /** * Returns a list of functions to add to the existing list. * - * @return array An array of functions + * @return Twig_SimpleFunction[] */ public function getFunctions(); /** * Returns a list of operators to add to the existing list. * - * @return array An array of operators + * @return array First array of unary operators, second array of binary operators */ public function getOperators(); @@ -71,6 +71,8 @@ interface Twig_ExtensionInterface * Returns a list of global variables to add to the existing list. * * @return array An array of global variables + * + * @deprecated since 1.23 (to be removed in 2.0), implement Twig_Extension_GlobalsInterface instead */ public function getGlobals(); @@ -78,6 +80,11 @@ interface Twig_ExtensionInterface * Returns the name of the extension. * * @return string The extension name + * + * @deprecated since 1.26 (to be removed in 2.0), not used anymore internally */ public function getName(); } + +class_alias('Twig_ExtensionInterface', 'Twig\Extension\ExtensionInterface', false); +class_exists('Twig_Environment'); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/FactoryRuntimeLoader.php b/lib/silex/vendor/twig/twig/lib/Twig/FactoryRuntimeLoader.php new file mode 100644 index 000000000..2cdaded1d --- /dev/null +++ b/lib/silex/vendor/twig/twig/lib/Twig/FactoryRuntimeLoader.php @@ -0,0 +1,39 @@ + + */ +class Twig_FactoryRuntimeLoader implements Twig_RuntimeLoaderInterface +{ + private $map; + + /** + * @param array $map An array where keys are class names and values factory callables + */ + public function __construct($map = array()) + { + $this->map = $map; + } + + public function load($class) + { + if (isset($this->map[$class])) { + $runtimeFactory = $this->map[$class]; + + return $runtimeFactory(); + } + } +} + +class_alias('Twig_FactoryRuntimeLoader', 'Twig\RuntimeLoader\FactoryRuntimeLoader', false); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/FileExtensionEscapingStrategy.php b/lib/silex/vendor/twig/twig/lib/Twig/FileExtensionEscapingStrategy.php index 9bda0b4f6..8f8cd2ee5 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/FileExtensionEscapingStrategy.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/FileExtensionEscapingStrategy.php @@ -3,7 +3,7 @@ /* * This file is part of Twig. * - * (c) 2015 Fabien Potencier + * (c) Fabien Potencier * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,7 +13,7 @@ * Default autoescaping strategy based on file names. * * This strategy sets the HTML as the default autoescaping strategy, - * but changes it based on the filename. + * but changes it based on the template name. * * Note that there is no runtime performance impact as the * default autoescaping strategy is set at compilation time. @@ -25,21 +25,21 @@ class Twig_FileExtensionEscapingStrategy /** * Guesses the best autoescaping strategy based on the file name. * - * @param string $filename The template file name + * @param string $name The template name * * @return string|false The escaping strategy name to use or false to disable */ - public static function guess($filename) + public static function guess($name) { - if (in_array(substr($filename, -1), array('/', '\\'))) { + if (in_array(substr($name, -1), array('/', '\\'))) { return 'html'; // return html for directories } - if ('.twig' === substr($filename, -5)) { - $filename = substr($filename, 0, -5); + if ('.twig' === substr($name, -5)) { + $name = substr($name, 0, -5); } - $extension = pathinfo($filename, PATHINFO_EXTENSION); + $extension = pathinfo($name, PATHINFO_EXTENSION); switch ($extension) { case 'js': @@ -56,3 +56,5 @@ class Twig_FileExtensionEscapingStrategy } } } + +class_alias('Twig_FileExtensionEscapingStrategy', 'Twig\FileExtensionEscapingStrategy', false); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/Filter.php b/lib/silex/vendor/twig/twig/lib/Twig/Filter.php index 101d2e793..893d75d10 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/Filter.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/Filter.php @@ -3,7 +3,7 @@ /* * This file is part of Twig. * - * (c) 2009 Fabien Potencier + * (c) Fabien Potencier * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. diff --git a/lib/silex/vendor/twig/twig/lib/Twig/Filter/Function.php b/lib/silex/vendor/twig/twig/lib/Twig/Filter/Function.php index d679cab2f..71b16554e 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/Filter/Function.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/Filter/Function.php @@ -3,7 +3,7 @@ /* * This file is part of Twig. * - * (c) 2009 Fabien Potencier + * (c) Fabien Potencier * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. diff --git a/lib/silex/vendor/twig/twig/lib/Twig/Filter/Method.php b/lib/silex/vendor/twig/twig/lib/Twig/Filter/Method.php index 655aab405..1b75676c5 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/Filter/Method.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/Filter/Method.php @@ -3,7 +3,7 @@ /* * This file is part of Twig. * - * (c) 2009 Fabien Potencier + * (c) Fabien Potencier * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -37,6 +37,6 @@ class Twig_Filter_Method extends Twig_Filter public function compile() { - return sprintf('$this->env->getExtension(\'%s\')->%s', $this->extension->getName(), $this->method); + return sprintf('$this->env->getExtension(\'%s\')->%s', get_class($this->extension), $this->method); } } diff --git a/lib/silex/vendor/twig/twig/lib/Twig/Filter/Node.php b/lib/silex/vendor/twig/twig/lib/Twig/Filter/Node.php index a922f5037..3e6b12eff 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/Filter/Node.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/Filter/Node.php @@ -3,7 +3,7 @@ /* * This file is part of Twig. * - * (c) 2011 Fabien Potencier + * (c) Fabien Potencier * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. diff --git a/lib/silex/vendor/twig/twig/lib/Twig/FilterCallableInterface.php b/lib/silex/vendor/twig/twig/lib/Twig/FilterCallableInterface.php index 567986131..21b028c4e 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/FilterCallableInterface.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/FilterCallableInterface.php @@ -3,7 +3,7 @@ /* * This file is part of Twig. * - * (c) 2012 Fabien Potencier + * (c) Fabien Potencier * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. diff --git a/lib/silex/vendor/twig/twig/lib/Twig/FilterInterface.php b/lib/silex/vendor/twig/twig/lib/Twig/FilterInterface.php index 6b0be0e30..9d7e9ab6a 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/FilterInterface.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/FilterInterface.php @@ -3,7 +3,7 @@ /* * This file is part of Twig. * - * (c) 2010 Fabien Potencier + * (c) Fabien Potencier * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. diff --git a/lib/silex/vendor/twig/twig/lib/Twig/Function.php b/lib/silex/vendor/twig/twig/lib/Twig/Function.php index 9fc76a8b7..9dc16e908 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/Function.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/Function.php @@ -3,7 +3,7 @@ /* * This file is part of Twig. * - * (c) 2010 Fabien Potencier + * (c) Fabien Potencier * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. diff --git a/lib/silex/vendor/twig/twig/lib/Twig/Function/Function.php b/lib/silex/vendor/twig/twig/lib/Twig/Function/Function.php index ae83e153c..97c0eb77f 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/Function/Function.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/Function/Function.php @@ -3,8 +3,8 @@ /* * This file is part of Twig. * - * (c) 2009 Fabien Potencier - * (c) 2010 Arnaud Le Blanc + * (c) Fabien Potencier + * (c) Arnaud Le Blanc * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. diff --git a/lib/silex/vendor/twig/twig/lib/Twig/Function/Method.php b/lib/silex/vendor/twig/twig/lib/Twig/Function/Method.php index ba9945ed5..4299e1181 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/Function/Method.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/Function/Method.php @@ -3,8 +3,8 @@ /* * This file is part of Twig. * - * (c) 2009 Fabien Potencier - * (c) 2010 Arnaud Le Blanc + * (c) Fabien Potencier + * (c) Arnaud Le Blanc * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -38,6 +38,6 @@ class Twig_Function_Method extends Twig_Function public function compile() { - return sprintf('$this->env->getExtension(\'%s\')->%s', $this->extension->getName(), $this->method); + return sprintf('$this->env->getExtension(\'%s\')->%s', get_class($this->extension), $this->method); } } diff --git a/lib/silex/vendor/twig/twig/lib/Twig/Function/Node.php b/lib/silex/vendor/twig/twig/lib/Twig/Function/Node.php index 118b0bab7..0adc5d937 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/Function/Node.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/Function/Node.php @@ -3,7 +3,7 @@ /* * This file is part of Twig. * - * (c) 2011 Fabien Potencier + * (c) Fabien Potencier * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. diff --git a/lib/silex/vendor/twig/twig/lib/Twig/FunctionCallableInterface.php b/lib/silex/vendor/twig/twig/lib/Twig/FunctionCallableInterface.php index 87d795eb8..d23d69173 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/FunctionCallableInterface.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/FunctionCallableInterface.php @@ -3,7 +3,7 @@ /* * This file is part of Twig. * - * (c) 2012 Fabien Potencier + * (c) Fabien Potencier * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. diff --git a/lib/silex/vendor/twig/twig/lib/Twig/FunctionInterface.php b/lib/silex/vendor/twig/twig/lib/Twig/FunctionInterface.php index f44923441..00d4f95c7 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/FunctionInterface.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/FunctionInterface.php @@ -3,8 +3,8 @@ /* * This file is part of Twig. * - * (c) 2010 Fabien Potencier - * (c) 2010 Arnaud Le Blanc + * (c) Fabien Potencier + * (c) Arnaud Le Blanc * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. diff --git a/lib/silex/vendor/twig/twig/lib/Twig/Lexer.php b/lib/silex/vendor/twig/twig/lib/Twig/Lexer.php index 75f763fb9..85390b28c 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/Lexer.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/Lexer.php @@ -3,8 +3,8 @@ /* * This file is part of Twig. * - * (c) 2009 Fabien Potencier - * (c) 2009 Armin Ronacher + * (c) Fabien Potencier + * (c) Armin Ronacher * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -26,6 +26,7 @@ class Twig_Lexer implements Twig_LexerInterface protected $states; protected $brackets; protected $env; + // to be renamed to $name in 2.0 (where it is private) protected $filename; protected $options; protected $regexes; @@ -33,6 +34,8 @@ class Twig_Lexer implements Twig_LexerInterface protected $positions; protected $currentVarBlockLine; + private $source; + const STATE_DATA = 0; const STATE_BLOCK = 1; const STATE_VAR = 2; @@ -72,11 +75,19 @@ class Twig_Lexer implements Twig_LexerInterface ); } - /** - * {@inheritdoc} - */ - public function tokenize($code, $filename = null) + public function tokenize($code, $name = null) { + if (!$code instanceof Twig_Source) { + @trigger_error(sprintf('Passing a string as the $code argument of %s() is deprecated since version 1.27 and will be removed in 2.0. Pass a Twig_Source instance instead.', __METHOD__), E_USER_DEPRECATED); + $this->source = new Twig_Source($code, $name); + } else { + $this->source = $code; + } + + if (((int) ini_get('mbstring.func_overload')) & 2) { + @trigger_error('Support for having "mbstring.func_overload" different from 0 is deprecated version 1.29 and will be removed in 2.0.', E_USER_DEPRECATED); + } + if (function_exists('mb_internal_encoding') && ((int) ini_get('mbstring.func_overload')) & 2) { $mbEncoding = mb_internal_encoding(); mb_internal_encoding('ASCII'); @@ -84,8 +95,8 @@ class Twig_Lexer implements Twig_LexerInterface $mbEncoding = null; } - $this->code = str_replace(array("\r\n", "\r"), "\n", $code); - $this->filename = $filename; + $this->code = str_replace(array("\r\n", "\r"), "\n", $this->source->getCode()); + $this->filename = $this->source->getName(); $this->cursor = 0; $this->lineno = 1; $this->end = strlen($this->code); @@ -129,14 +140,14 @@ class Twig_Lexer implements Twig_LexerInterface if (!empty($this->brackets)) { list($expect, $lineno) = array_pop($this->brackets); - throw new Twig_Error_Syntax(sprintf('Unclosed "%s"', $expect), $lineno, $this->filename); + throw new Twig_Error_Syntax(sprintf('Unclosed "%s".', $expect), $lineno, $this->source); } if ($mbEncoding) { mb_internal_encoding($mbEncoding); } - return new Twig_TokenStream($this->tokens, $this->filename); + return new Twig_TokenStream($this->tokens, $this->source); } protected function lexData() @@ -224,7 +235,7 @@ class Twig_Lexer implements Twig_LexerInterface $this->moveCursor($match[0]); if ($this->cursor >= $this->end) { - throw new Twig_Error_Syntax(sprintf('Unclosed "%s"', $this->state === self::STATE_BLOCK ? 'block' : 'variable'), $this->currentVarBlockLine, $this->filename); + throw new Twig_Error_Syntax(sprintf('Unclosed "%s".', $this->state === self::STATE_BLOCK ? 'block' : 'variable'), $this->currentVarBlockLine, $this->source); } } @@ -256,12 +267,12 @@ class Twig_Lexer implements Twig_LexerInterface // closing bracket elseif (false !== strpos(')]}', $this->code[$this->cursor])) { if (empty($this->brackets)) { - throw new Twig_Error_Syntax(sprintf('Unexpected "%s"', $this->code[$this->cursor]), $this->lineno, $this->filename); + throw new Twig_Error_Syntax(sprintf('Unexpected "%s".', $this->code[$this->cursor]), $this->lineno, $this->source); } list($expect, $lineno) = array_pop($this->brackets); if ($this->code[$this->cursor] != strtr($expect, '([{', ')]}')) { - throw new Twig_Error_Syntax(sprintf('Unclosed "%s"', $expect), $lineno, $this->filename); + throw new Twig_Error_Syntax(sprintf('Unclosed "%s".', $expect), $lineno, $this->source); } } @@ -281,18 +292,18 @@ class Twig_Lexer implements Twig_LexerInterface } // unlexable else { - throw new Twig_Error_Syntax(sprintf('Unexpected character "%s"', $this->code[$this->cursor]), $this->lineno, $this->filename); + throw new Twig_Error_Syntax(sprintf('Unexpected character "%s".', $this->code[$this->cursor]), $this->lineno, $this->source); } } protected function lexRawData($tag) { if ('raw' === $tag) { - @trigger_error(sprintf('Twig Tag "raw" is deprecated. Use "verbatim" instead in %s at line %d.', $this->filename, $this->lineno), E_USER_DEPRECATED); + @trigger_error(sprintf('Twig Tag "raw" is deprecated since version 1.21. Use "verbatim" instead in %s at line %d.', $this->filename, $this->lineno), E_USER_DEPRECATED); } if (!preg_match(str_replace('%s', $tag, $this->regexes['lex_raw_data']), $this->code, $match, PREG_OFFSET_CAPTURE, $this->cursor)) { - throw new Twig_Error_Syntax(sprintf('Unexpected end of file: Unclosed "%s" block', $tag), $this->lineno, $this->filename); + throw new Twig_Error_Syntax(sprintf('Unexpected end of file: Unclosed "%s" block.', $tag), $this->lineno, $this->source); } $text = substr($this->code, $this->cursor, $match[0][1] - $this->cursor); @@ -308,7 +319,7 @@ class Twig_Lexer implements Twig_LexerInterface protected function lexComment() { if (!preg_match($this->regexes['lex_comment'], $this->code, $match, PREG_OFFSET_CAPTURE, $this->cursor)) { - throw new Twig_Error_Syntax('Unclosed comment', $this->lineno, $this->filename); + throw new Twig_Error_Syntax('Unclosed comment.', $this->lineno, $this->source); } $this->moveCursor(substr($this->code, $this->cursor, $match[0][1] - $this->cursor).$match[0][0]); @@ -327,7 +338,7 @@ class Twig_Lexer implements Twig_LexerInterface } elseif (preg_match(self::REGEX_DQ_STRING_DELIM, $this->code, $match, null, $this->cursor)) { list($expect, $lineno) = array_pop($this->brackets); if ($this->code[$this->cursor] != '"') { - throw new Twig_Error_Syntax(sprintf('Unclosed "%s"', $expect), $lineno, $this->filename); + throw new Twig_Error_Syntax(sprintf('Unclosed "%s".', $expect), $lineno, $this->source); } $this->popState(); @@ -403,9 +414,11 @@ class Twig_Lexer implements Twig_LexerInterface protected function popState() { if (0 === count($this->states)) { - throw new Exception('Cannot pop state without a previous state'); + throw new Exception('Cannot pop state without a previous state.'); } $this->state = array_pop($this->states); } } + +class_alias('Twig_Lexer', 'Twig\Lexer', false); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/LexerInterface.php b/lib/silex/vendor/twig/twig/lib/Twig/LexerInterface.php index 24a947870..c10bbfec4 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/LexerInterface.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/LexerInterface.php @@ -3,7 +3,7 @@ /* * This file is part of Twig. * - * (c) 2009 Fabien Potencier + * (c) Fabien Potencier * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -21,12 +21,12 @@ interface Twig_LexerInterface /** * Tokenizes a source code. * - * @param string $code The source code - * @param string $filename A unique identifier for the source code + * @param string|Twig_Source $code The source code + * @param string $name A unique identifier for the source code * - * @return Twig_TokenStream A token stream instance + * @return Twig_TokenStream * * @throws Twig_Error_Syntax When the code is syntactically wrong */ - public function tokenize($code, $filename = null); + public function tokenize($code, $name = null); } diff --git a/lib/silex/vendor/twig/twig/lib/Twig/Loader/Array.php b/lib/silex/vendor/twig/twig/lib/Twig/Loader/Array.php index 90221d5db..0aac76900 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/Loader/Array.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/Loader/Array.php @@ -3,7 +3,7 @@ /* * This file is part of Twig. * - * (c) 2009 Fabien Potencier + * (c) Fabien Potencier * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -19,18 +19,18 @@ * * This loader should only be used for unit testing. * + * @final + * * @author Fabien Potencier */ -class Twig_Loader_Array implements Twig_LoaderInterface, Twig_ExistsLoaderInterface +class Twig_Loader_Array implements Twig_LoaderInterface, Twig_ExistsLoaderInterface, Twig_SourceContextLoaderInterface { protected $templates = array(); /** - * Constructor. - * * @param array $templates An array of templates (keys are the names, and values are the source code) */ - public function __construct(array $templates) + public function __construct(array $templates = array()) { $this->templates = $templates; } @@ -46,11 +46,10 @@ class Twig_Loader_Array implements Twig_LoaderInterface, Twig_ExistsLoaderInterf $this->templates[(string) $name] = $template; } - /** - * {@inheritdoc} - */ public function getSource($name) { + @trigger_error(sprintf('Calling "getSource" on "%s" is deprecated since 1.27. Use getSourceContext() instead.', get_class($this)), E_USER_DEPRECATED); + $name = (string) $name; if (!isset($this->templates[$name])) { throw new Twig_Error_Loader(sprintf('Template "%s" is not defined.', $name)); @@ -59,17 +58,21 @@ class Twig_Loader_Array implements Twig_LoaderInterface, Twig_ExistsLoaderInterf return $this->templates[$name]; } - /** - * {@inheritdoc} - */ + public function getSourceContext($name) + { + $name = (string) $name; + if (!isset($this->templates[$name])) { + throw new Twig_Error_Loader(sprintf('Template "%s" is not defined.', $name)); + } + + return new Twig_Source($this->templates[$name], $name); + } + public function exists($name) { return isset($this->templates[(string) $name]); } - /** - * {@inheritdoc} - */ public function getCacheKey($name) { $name = (string) $name; @@ -77,12 +80,9 @@ class Twig_Loader_Array implements Twig_LoaderInterface, Twig_ExistsLoaderInterf throw new Twig_Error_Loader(sprintf('Template "%s" is not defined.', $name)); } - return $this->templates[$name]; + return $name.':'.$this->templates[$name]; } - /** - * {@inheritdoc} - */ public function isFresh($name, $time) { $name = (string) $name; @@ -93,3 +93,5 @@ class Twig_Loader_Array implements Twig_LoaderInterface, Twig_ExistsLoaderInterf return true; } } + +class_alias('Twig_Loader_Array', 'Twig\Loader\ArrayLoader', false); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/Loader/Chain.php b/lib/silex/vendor/twig/twig/lib/Twig/Loader/Chain.php index 7919eda62..59a337969 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/Loader/Chain.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/Loader/Chain.php @@ -3,7 +3,7 @@ /* * This file is part of Twig. * - * (c) 2011 Fabien Potencier + * (c) Fabien Potencier * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -12,17 +12,17 @@ /** * Loads templates from other loaders. * + * @final + * * @author Fabien Potencier */ -class Twig_Loader_Chain implements Twig_LoaderInterface, Twig_ExistsLoaderInterface +class Twig_Loader_Chain implements Twig_LoaderInterface, Twig_ExistsLoaderInterface, Twig_SourceContextLoaderInterface { private $hasSourceCache = array(); protected $loaders = array(); /** - * Constructor. - * - * @param Twig_LoaderInterface[] $loaders An array of loader instances + * @param Twig_LoaderInterface[] $loaders */ public function __construct(array $loaders = array()) { @@ -31,22 +31,16 @@ class Twig_Loader_Chain implements Twig_LoaderInterface, Twig_ExistsLoaderInterf } } - /** - * Adds a loader instance. - * - * @param Twig_LoaderInterface $loader A Loader instance - */ public function addLoader(Twig_LoaderInterface $loader) { $this->loaders[] = $loader; $this->hasSourceCache = array(); } - /** - * {@inheritdoc} - */ public function getSource($name) { + @trigger_error(sprintf('Calling "getSource" on "%s" is deprecated since 1.27. Use getSourceContext() instead.', get_class($this)), E_USER_DEPRECATED); + $exceptions = array(); foreach ($this->loaders as $loader) { if ($loader instanceof Twig_ExistsLoaderInterface && !$loader->exists($name)) { @@ -60,12 +54,31 @@ class Twig_Loader_Chain implements Twig_LoaderInterface, Twig_ExistsLoaderInterf } } - throw new Twig_Error_Loader(sprintf('Template "%s" is not defined (%s).', $name, implode(', ', $exceptions))); + throw new Twig_Error_Loader(sprintf('Template "%s" is not defined%s.', $name, $exceptions ? ' ('.implode(', ', $exceptions).')' : '')); + } + + public function getSourceContext($name) + { + $exceptions = array(); + foreach ($this->loaders as $loader) { + if ($loader instanceof Twig_ExistsLoaderInterface && !$loader->exists($name)) { + continue; + } + + try { + if ($loader instanceof Twig_SourceContextLoaderInterface) { + return $loader->getSourceContext($name); + } + + return new Twig_Source($loader->getSource($name), $name); + } catch (Twig_Error_Loader $e) { + $exceptions[] = $e->getMessage(); + } + } + + throw new Twig_Error_Loader(sprintf('Template "%s" is not defined%s.', $name, $exceptions ? ' ('.implode(', ', $exceptions).')' : '')); } - /** - * {@inheritdoc} - */ public function exists($name) { $name = (string) $name; @@ -84,7 +97,11 @@ class Twig_Loader_Chain implements Twig_LoaderInterface, Twig_ExistsLoaderInterf } try { - $loader->getSource($name); + if ($loader instanceof Twig_SourceContextLoaderInterface) { + $loader->getSourceContext($name); + } else { + $loader->getSource($name); + } return $this->hasSourceCache[$name] = true; } catch (Twig_Error_Loader $e) { @@ -94,9 +111,6 @@ class Twig_Loader_Chain implements Twig_LoaderInterface, Twig_ExistsLoaderInterf return $this->hasSourceCache[$name] = false; } - /** - * {@inheritdoc} - */ public function getCacheKey($name) { $exceptions = array(); @@ -112,12 +126,9 @@ class Twig_Loader_Chain implements Twig_LoaderInterface, Twig_ExistsLoaderInterf } } - throw new Twig_Error_Loader(sprintf('Template "%s" is not defined (%s).', $name, implode(' ', $exceptions))); + throw new Twig_Error_Loader(sprintf('Template "%s" is not defined%s.', $name, $exceptions ? ' ('.implode(', ', $exceptions).')' : '')); } - /** - * {@inheritdoc} - */ public function isFresh($name, $time) { $exceptions = array(); @@ -133,6 +144,8 @@ class Twig_Loader_Chain implements Twig_LoaderInterface, Twig_ExistsLoaderInterf } } - throw new Twig_Error_Loader(sprintf('Template "%s" is not defined (%s).', $name, implode(' ', $exceptions))); + throw new Twig_Error_Loader(sprintf('Template "%s" is not defined%s.', $name, $exceptions ? ' ('.implode(', ', $exceptions).')' : '')); } } + +class_alias('Twig_Loader_Chain', 'Twig\Loader\ChainLoader', false); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/Loader/Filesystem.php b/lib/silex/vendor/twig/twig/lib/Twig/Loader/Filesystem.php index a68876cee..1275044f8 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/Loader/Filesystem.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/Loader/Filesystem.php @@ -3,7 +3,7 @@ /* * This file is part of Twig. * - * (c) 2009 Fabien Potencier + * (c) Fabien Potencier * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,7 @@ * * @author Fabien Potencier */ -class Twig_Loader_Filesystem implements Twig_LoaderInterface, Twig_ExistsLoaderInterface +class Twig_Loader_Filesystem implements Twig_LoaderInterface, Twig_ExistsLoaderInterface, Twig_SourceContextLoaderInterface { /** Identifier of the main namespace. */ const MAIN_NAMESPACE = '__main__'; @@ -23,13 +23,19 @@ class Twig_Loader_Filesystem implements Twig_LoaderInterface, Twig_ExistsLoaderI protected $cache = array(); protected $errorCache = array(); + private $rootPath; + /** - * Constructor. - * - * @param string|array $paths A path or an array of paths where to look for templates + * @param string|array $paths A path or an array of paths where to look for templates + * @param string|null $rootPath The root path common to all relative paths (null for getcwd()) */ - public function __construct($paths = array()) + public function __construct($paths = array(), $rootPath = null) { + $this->rootPath = (null === $rootPath ? getcwd() : $rootPath).DIRECTORY_SEPARATOR; + if (false !== $realPath = realpath($rootPath)) { + $this->rootPath = $realPath.DIRECTORY_SEPARATOR; + } + if ($paths) { $this->setPaths($paths); } @@ -81,7 +87,7 @@ class Twig_Loader_Filesystem implements Twig_LoaderInterface, Twig_ExistsLoaderI * Adds a path where templates are stored. * * @param string $path A path where to look for templates - * @param string $namespace A path name + * @param string $namespace A path namespace * * @throws Twig_Error_Loader */ @@ -90,8 +96,9 @@ class Twig_Loader_Filesystem implements Twig_LoaderInterface, Twig_ExistsLoaderI // invalidate the cache $this->cache = $this->errorCache = array(); - if (!is_dir($path)) { - throw new Twig_Error_Loader(sprintf('The "%s" directory does not exist.', $path)); + $checkPath = $this->isAbsolutePath($path) ? $path : $this->rootPath.$path; + if (!is_dir($checkPath)) { + throw new Twig_Error_Loader(sprintf('The "%s" directory does not exist ("%s").', $path, $checkPath)); } $this->paths[$namespace][] = rtrim($path, '/\\'); @@ -101,7 +108,7 @@ class Twig_Loader_Filesystem implements Twig_LoaderInterface, Twig_ExistsLoaderI * Prepends a path where templates are stored. * * @param string $path A path where to look for templates - * @param string $namespace A path name + * @param string $namespace A path namespace * * @throws Twig_Error_Loader */ @@ -110,8 +117,9 @@ class Twig_Loader_Filesystem implements Twig_LoaderInterface, Twig_ExistsLoaderI // invalidate the cache $this->cache = $this->errorCache = array(); - if (!is_dir($path)) { - throw new Twig_Error_Loader(sprintf('The "%s" directory does not exist.', $path)); + $checkPath = $this->isAbsolutePath($path) ? $path : $this->rootPath.$path; + if (!is_dir($checkPath)) { + throw new Twig_Error_Loader(sprintf('The "%s" directory does not exist ("%s").', $path, $checkPath)); } $path = rtrim($path, '/\\'); @@ -123,25 +131,31 @@ class Twig_Loader_Filesystem implements Twig_LoaderInterface, Twig_ExistsLoaderI } } - /** - * {@inheritdoc} - */ public function getSource($name) { + @trigger_error(sprintf('Calling "getSource" on "%s" is deprecated since 1.27. Use getSourceContext() instead.', get_class($this)), E_USER_DEPRECATED); + return file_get_contents($this->findTemplate($name)); } - /** - * {@inheritdoc} - */ - public function getCacheKey($name) + public function getSourceContext($name) { - return $this->findTemplate($name); + $path = $this->findTemplate($name); + + return new Twig_Source(file_get_contents($path), $name, $path); + } + + public function getCacheKey($name) + { + $path = $this->findTemplate($name); + $len = strlen($this->rootPath); + if (0 === strncmp($this->rootPath, $path, $len)) { + return substr($path, $len); + } + + return $path; } - /** - * {@inheritdoc} - */ public function exists($name) { $name = $this->normalizeName($name); @@ -153,13 +167,12 @@ class Twig_Loader_Filesystem implements Twig_LoaderInterface, Twig_ExistsLoaderI try { return false !== $this->findTemplate($name, false); } catch (Twig_Error_Loader $exception) { + @trigger_error(sprintf('In %s::findTemplate(), you must accept a second argument that when set to "false" returns "false" instead of throwing an exception. Not supporting this argument is deprecated since version 1.27.', get_class($this)), E_USER_DEPRECATED); + return false; } } - /** - * {@inheritdoc} - */ public function isFresh($name, $time) { return filemtime($this->findTemplate($name)) <= $time; @@ -197,6 +210,10 @@ class Twig_Loader_Filesystem implements Twig_LoaderInterface, Twig_ExistsLoaderI } foreach ($this->paths[$namespace] as $path) { + if (!$this->isAbsolutePath($path)) { + $path = $this->rootPath.'/'.$path; + } + if (is_file($path.'/'.$shortname)) { if (false !== $realpath = realpath($path.'/'.$shortname)) { return $this->cache[$name] = $realpath; @@ -233,7 +250,7 @@ class Twig_Loader_Filesystem implements Twig_LoaderInterface, Twig_ExistsLoaderI protected function normalizeName($name) { - return preg_replace('#/{2,}#', '/', strtr((string) $name, '\\', '/')); + return preg_replace('#/{2,}#', '/', str_replace('\\', '/', (string) $name)); } protected function validateName($name) @@ -257,4 +274,17 @@ class Twig_Loader_Filesystem implements Twig_LoaderInterface, Twig_ExistsLoaderI } } } + + private function isAbsolutePath($file) + { + return strspn($file, '/\\', 0, 1) + || (strlen($file) > 3 && ctype_alpha($file[0]) + && substr($file, 1, 1) === ':' + && strspn($file, '/\\', 2, 1) + ) + || null !== parse_url($file, PHP_URL_SCHEME) + ; + } } + +class_alias('Twig_Loader_Filesystem', 'Twig\Loader\FilesystemLoader', false); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/Loader/String.php b/lib/silex/vendor/twig/twig/lib/Twig/Loader/String.php index e7fff3e81..950bd35b0 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/Loader/String.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/Loader/String.php @@ -3,13 +3,13 @@ /* * This file is part of Twig. * - * (c) 2009 Fabien Potencier + * (c) Fabien Potencier * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -@trigger_error('The Twig_Loader_String class is deprecated since version 1.18.1 and will be removed in 2.0. Use Twig_Loader_Array instead.', E_USER_DEPRECATED); +@trigger_error('The Twig_Loader_String class is deprecated since version 1.18.1 and will be removed in 2.0. Use Twig_Loader_Array instead or Twig_Environment::createTemplate().', E_USER_DEPRECATED); /** * Loads a template from a string. @@ -23,37 +23,34 @@ * * @deprecated since 1.18.1 (to be removed in 2.0) * + * @internal + * * @author Fabien Potencier */ -class Twig_Loader_String implements Twig_LoaderInterface, Twig_ExistsLoaderInterface +class Twig_Loader_String implements Twig_LoaderInterface, Twig_ExistsLoaderInterface, Twig_SourceContextLoaderInterface { - /** - * {@inheritdoc} - */ public function getSource($name) { + @trigger_error(sprintf('Calling "getSource" on "%s" is deprecated since 1.27. Use getSourceContext() instead.', get_class($this)), E_USER_DEPRECATED); + return $name; } - /** - * {@inheritdoc} - */ + public function getSourceContext($name) + { + return new Twig_Source($name, $name); + } + public function exists($name) { return true; } - /** - * {@inheritdoc} - */ public function getCacheKey($name) { return $name; } - /** - * {@inheritdoc} - */ public function isFresh($name, $time) { return true; diff --git a/lib/silex/vendor/twig/twig/lib/Twig/LoaderInterface.php b/lib/silex/vendor/twig/twig/lib/Twig/LoaderInterface.php index 544ea4e32..459a70abb 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/LoaderInterface.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/LoaderInterface.php @@ -3,7 +3,7 @@ /* * This file is part of Twig. * - * (c) 2009 Fabien Potencier + * (c) Fabien Potencier * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -24,6 +24,8 @@ interface Twig_LoaderInterface * @return string The template source code * * @throws Twig_Error_Loader When $name is not found + * + * @deprecated since 1.27 (to be removed in 2.0), implement Twig_SourceContextLoaderInterface */ public function getSource($name); @@ -51,3 +53,5 @@ interface Twig_LoaderInterface */ public function isFresh($name, $time); } + +class_alias('Twig_LoaderInterface', 'Twig\Loader\LoaderInterface', false); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/Markup.php b/lib/silex/vendor/twig/twig/lib/Twig/Markup.php index 69871fcbd..8591d1f93 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/Markup.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/Markup.php @@ -3,7 +3,7 @@ /* * This file is part of Twig. * - * (c) 2010 Fabien Potencier + * (c) Fabien Potencier * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -35,3 +35,5 @@ class Twig_Markup implements Countable return function_exists('mb_get_info') ? mb_strlen($this->content, $this->charset) : strlen($this->content); } } + +class_alias('Twig_Markup', 'Twig\Markup', false); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/Node.php b/lib/silex/vendor/twig/twig/lib/Twig/Node.php index 40d67fe59..89ada1449 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/Node.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/Node.php @@ -3,8 +3,8 @@ /* * This file is part of Twig. * - * (c) 2009 Fabien Potencier - * (c) 2009 Armin Ronacher + * (c) Fabien Potencier + * (c) Armin Ronacher * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -22,6 +22,8 @@ class Twig_Node implements Twig_NodeInterface protected $lineno; protected $tag; + private $name; + /** * Constructor. * @@ -35,6 +37,11 @@ class Twig_Node implements Twig_NodeInterface */ public function __construct(array $nodes = array(), array $attributes = array(), $lineno = 0, $tag = null) { + foreach ($nodes as $name => $node) { + if (!$node instanceof Twig_NodeInterface) { + @trigger_error(sprintf('Using "%s" for the value of node "%s" of "%s" is deprecated since version 1.25 and will be removed in 2.0.', is_object($node) ? get_class($node) : null === $node ? 'null' : gettype($node), $name, get_class($this)), E_USER_DEPRECATED); + } + } $this->nodes = $nodes; $this->attributes = $attributes; $this->lineno = $lineno; @@ -74,7 +81,7 @@ class Twig_Node implements Twig_NodeInterface */ public function toXml($asDom = false) { - @trigger_error(sprintf('%s is deprecated.', __METHOD__), E_USER_DEPRECATED); + @trigger_error(sprintf('%s is deprecated since version 1.16.1 and will be removed in 2.0.', __METHOD__), E_USER_DEPRECATED); $dom = new DOMDocument('1.0', 'UTF-8'); $dom->formatOutput = true; @@ -101,7 +108,7 @@ class Twig_Node implements Twig_NodeInterface $node->appendChild($child); } - return $asDom ? $dom : $dom->saveXml(); + return $asDom ? $dom : $dom->saveXML(); } public function compile(Twig_Compiler $compiler) @@ -111,8 +118,18 @@ class Twig_Node implements Twig_NodeInterface } } + public function getTemplateLine() + { + return $this->lineno; + } + + /** + * @deprecated since 1.27 (to be removed in 2.0) + */ public function getLine() { + @trigger_error('The '.__METHOD__.' method is deprecated since version 1.27 and will be removed in 2.0. Use getTemplateLine() instead.', E_USER_DEPRECATED); + return $this->lineno; } @@ -122,11 +139,7 @@ class Twig_Node implements Twig_NodeInterface } /** - * Returns true if the attribute is defined. - * - * @param string $name The attribute name - * - * @return bool true if the attribute is defined, false otherwise + * @return bool */ public function hasAttribute($name) { @@ -134,10 +147,6 @@ class Twig_Node implements Twig_NodeInterface } /** - * Gets an attribute value by name. - * - * @param string $name - * * @return mixed */ public function getAttribute($name) @@ -150,8 +159,6 @@ class Twig_Node implements Twig_NodeInterface } /** - * Sets an attribute by name to a value. - * * @param string $name * @param mixed $value */ @@ -160,21 +167,12 @@ class Twig_Node implements Twig_NodeInterface $this->attributes[$name] = $value; } - /** - * Removes an attribute by name. - * - * @param string $name - */ public function removeAttribute($name) { unset($this->attributes[$name]); } /** - * Returns true if the node with the given name exists. - * - * @param string $name - * * @return bool */ public function hasNode($name) @@ -183,10 +181,6 @@ class Twig_Node implements Twig_NodeInterface } /** - * Gets a node by name. - * - * @param string $name - * * @return Twig_Node */ public function getNode($name) @@ -198,22 +192,15 @@ class Twig_Node implements Twig_NodeInterface return $this->nodes[$name]; } - /** - * Sets a node. - * - * @param string $name - * @param Twig_Node $node - */ public function setNode($name, $node = null) { + if (!$node instanceof Twig_NodeInterface) { + @trigger_error(sprintf('Using "%s" for the value of node "%s" of "%s" is deprecated since version 1.25 and will be removed in 2.0.', is_object($node) ? get_class($node) : null === $node ? 'null' : gettype($node), $name, get_class($this)), E_USER_DEPRECATED); + } + $this->nodes[$name] = $node; } - /** - * Removes a node by name. - * - * @param string $name - */ public function removeNode($name) { unset($this->nodes[$name]); @@ -228,4 +215,42 @@ class Twig_Node implements Twig_NodeInterface { return new ArrayIterator($this->nodes); } + + public function setTemplateName($name) + { + $this->name = $name; + foreach ($this->nodes as $node) { + if (null !== $node) { + $node->setTemplateName($name); + } + } + } + + public function getTemplateName() + { + return $this->name; + } + + /** + * @deprecated since 1.27 (to be removed in 2.0) + */ + public function setFilename($name) + { + @trigger_error('The '.__METHOD__.' method is deprecated since version 1.27 and will be removed in 2.0. Use setTemplateName() instead.', E_USER_DEPRECATED); + + $this->setTemplateName($name); + } + + /** + * @deprecated since 1.27 (to be removed in 2.0) + */ + public function getFilename() + { + @trigger_error('The '.__METHOD__.' method is deprecated since version 1.27 and will be removed in 2.0. Use getTemplateName() instead.', E_USER_DEPRECATED); + + return $this->name; + } } + +class_alias('Twig_Node', 'Twig\Node\Node', false); +class_exists('Twig_Compiler'); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/Node/AutoEscape.php b/lib/silex/vendor/twig/twig/lib/Twig/Node/AutoEscape.php index fcabf9033..17e4e3813 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/Node/AutoEscape.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/Node/AutoEscape.php @@ -3,7 +3,7 @@ /* * This file is part of Twig. * - * (c) 2009 Fabien Potencier + * (c) Fabien Potencier * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -27,13 +27,10 @@ class Twig_Node_AutoEscape extends Twig_Node parent::__construct(array('body' => $body), array('value' => $value), $lineno, $tag); } - /** - * Compiles the node to PHP. - * - * @param Twig_Compiler $compiler A Twig_Compiler instance - */ public function compile(Twig_Compiler $compiler) { $compiler->subcompile($this->getNode('body')); } } + +class_alias('Twig_Node_AutoEscape', 'Twig\Node\AutoEscapeNode', false); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/Node/Block.php b/lib/silex/vendor/twig/twig/lib/Twig/Node/Block.php index 989e4a0ca..91752ad27 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/Node/Block.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/Node/Block.php @@ -3,8 +3,8 @@ /* * This file is part of Twig. * - * (c) 2009 Fabien Potencier - * (c) 2009 Armin Ronacher + * (c) Fabien Potencier + * (c) Armin Ronacher * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -22,11 +22,6 @@ class Twig_Node_Block extends Twig_Node parent::__construct(array('body' => $body), array('name' => $name), $lineno, $tag); } - /** - * Compiles the node to PHP. - * - * @param Twig_Compiler $compiler A Twig_Compiler instance - */ public function compile(Twig_Compiler $compiler) { $compiler @@ -42,3 +37,5 @@ class Twig_Node_Block extends Twig_Node ; } } + +class_alias('Twig_Node_Block', 'Twig\Node\BlockNode', false); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/Node/BlockReference.php b/lib/silex/vendor/twig/twig/lib/Twig/Node/BlockReference.php index a05ea045b..92a9f398a 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/Node/BlockReference.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/Node/BlockReference.php @@ -3,8 +3,8 @@ /* * This file is part of Twig. * - * (c) 2009 Fabien Potencier - * (c) 2009 Armin Ronacher + * (c) Fabien Potencier + * (c) Armin Ronacher * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -22,11 +22,6 @@ class Twig_Node_BlockReference extends Twig_Node implements Twig_NodeOutputInter parent::__construct(array(), array('name' => $name), $lineno, $tag); } - /** - * Compiles the node to PHP. - * - * @param Twig_Compiler $compiler A Twig_Compiler instance - */ public function compile(Twig_Compiler $compiler) { $compiler @@ -35,3 +30,5 @@ class Twig_Node_BlockReference extends Twig_Node implements Twig_NodeOutputInter ; } } + +class_alias('Twig_Node_BlockReference', 'Twig\Node\BlockReferenceNode', false); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/Node/Body.php b/lib/silex/vendor/twig/twig/lib/Twig/Node/Body.php index 3ffb1342b..07dfef8b5 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/Node/Body.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/Node/Body.php @@ -3,7 +3,7 @@ /* * This file is part of Twig. * - * (c) 2011 Fabien Potencier + * (c) Fabien Potencier * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -17,3 +17,5 @@ class Twig_Node_Body extends Twig_Node { } + +class_alias('Twig_Node_Body', 'Twig\Node\BodyNode', false); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/Node/CheckSecurity.php b/lib/silex/vendor/twig/twig/lib/Twig/Node/CheckSecurity.php index b4a436ab1..7258acb6a 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/Node/CheckSecurity.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/Node/CheckSecurity.php @@ -3,7 +3,7 @@ /* * This file is part of Twig. * - * (c) 2015 Fabien Potencier + * (c) Fabien Potencier * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -33,7 +33,7 @@ class Twig_Node_CheckSecurity extends Twig_Node foreach (array('tags', 'filters', 'functions') as $type) { foreach ($this->{'used'.ucfirst($type)} as $name => $node) { if ($node instanceof Twig_Node) { - ${$type}[$name] = $node->getLine(); + ${$type}[$name] = $node->getTemplateLine(); } else { ${$type}[$node] = null; } @@ -46,7 +46,7 @@ class Twig_Node_CheckSecurity extends Twig_Node ->write('$functions = ')->repr(array_filter($functions))->raw(";\n\n") ->write("try {\n") ->indent() - ->write("\$this->env->getExtension('sandbox')->checkSecurity(\n") + ->write("\$this->env->getExtension('Twig_Extension_Sandbox')->checkSecurity(\n") ->indent() ->write(!$tags ? "array(),\n" : "array('".implode("', '", array_keys($tags))."'),\n") ->write(!$filters ? "array(),\n" : "array('".implode("', '", array_keys($filters))."'),\n") @@ -56,7 +56,7 @@ class Twig_Node_CheckSecurity extends Twig_Node ->outdent() ->write("} catch (Twig_Sandbox_SecurityError \$e) {\n") ->indent() - ->write("\$e->setTemplateFile(\$this->getTemplateName());\n\n") + ->write("\$e->setSourceContext(\$this->getSourceContext());\n\n") ->write("if (\$e instanceof Twig_Sandbox_SecurityNotAllowedTagError && isset(\$tags[\$e->getTagName()])) {\n") ->indent() ->write("\$e->setTemplateLine(\$tags[\$e->getTagName()]);\n") @@ -76,3 +76,5 @@ class Twig_Node_CheckSecurity extends Twig_Node ; } } + +class_alias('Twig_Node_CheckSecurity', 'Twig\Node\CheckSecurityNode', false); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/Node/Do.php b/lib/silex/vendor/twig/twig/lib/Twig/Node/Do.php index 9981bc16b..cdd7e77a4 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/Node/Do.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/Node/Do.php @@ -3,7 +3,7 @@ /* * This file is part of Twig. * - * (c) 2011 Fabien Potencier + * (c) Fabien Potencier * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -21,11 +21,6 @@ class Twig_Node_Do extends Twig_Node parent::__construct(array('expr' => $expr), array(), $lineno, $tag); } - /** - * Compiles the node to PHP. - * - * @param Twig_Compiler $compiler A Twig_Compiler instance - */ public function compile(Twig_Compiler $compiler) { $compiler @@ -36,3 +31,5 @@ class Twig_Node_Do extends Twig_Node ; } } + +class_alias('Twig_Node_Do', 'Twig\Node\DoNode', false); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/Node/Embed.php b/lib/silex/vendor/twig/twig/lib/Twig/Node/Embed.php index a21304030..3785d3a9f 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/Node/Embed.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/Node/Embed.php @@ -3,7 +3,7 @@ /* * This file is part of Twig. * - * (c) 2012 Fabien Potencier + * (c) Fabien Potencier * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -17,11 +17,13 @@ class Twig_Node_Embed extends Twig_Node_Include { // we don't inject the module to avoid node visitors to traverse it twice (as it will be already visited in the main module) - public function __construct($filename, $index, Twig_Node_Expression $variables = null, $only = false, $ignoreMissing = false, $lineno, $tag = null) + public function __construct($name, $index, Twig_Node_Expression $variables = null, $only = false, $ignoreMissing = false, $lineno, $tag = null) { parent::__construct(new Twig_Node_Expression_Constant('not_used', $lineno), $variables, $only, $ignoreMissing, $lineno, $tag); - $this->setAttribute('filename', $filename); + $this->setAttribute('name', $name); + // to be removed in 2.0, used name instead + $this->setAttribute('filename', $name); $this->setAttribute('index', $index); } @@ -29,14 +31,16 @@ class Twig_Node_Embed extends Twig_Node_Include { $compiler ->write('$this->loadTemplate(') - ->string($this->getAttribute('filename')) + ->string($this->getAttribute('name')) ->raw(', ') - ->repr($compiler->getFilename()) + ->repr($this->getTemplateName()) ->raw(', ') - ->repr($this->getLine()) + ->repr($this->getTemplateLine()) ->raw(', ') ->string($this->getAttribute('index')) ->raw(')') ; } } + +class_alias('Twig_Node_Embed', 'Twig\Node\EmbedNode', false); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression.php b/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression.php index a7382e7d6..a99c4e63a 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression.php @@ -3,8 +3,8 @@ /* * This file is part of Twig. * - * (c) 2009 Fabien Potencier - * (c) 2009 Armin Ronacher + * (c) Fabien Potencier + * (c) Armin Ronacher * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -18,3 +18,5 @@ abstract class Twig_Node_Expression extends Twig_Node { } + +class_alias('Twig_Node_Expression', 'Twig\Node\Expression\AbstractExpression', false); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Array.php b/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Array.php index 6cf7ca144..0e77bb08b 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Array.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Array.php @@ -3,7 +3,7 @@ /* * This file is part of Twig. * - * (c) 2009 Fabien Potencier + * (c) Fabien Potencier * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -43,7 +43,7 @@ class Twig_Node_Expression_Array extends Twig_Node_Expression foreach ($this->getKeyValuePairs() as $pair) { // we compare the string representation of the keys // to avoid comparing the line numbers which are not relevant here. - if ((string) $key == (string) $pair['key']) { + if ((string) $key === (string) $pair['key']) { return true; } } @@ -54,17 +54,12 @@ class Twig_Node_Expression_Array extends Twig_Node_Expression public function addElement(Twig_Node_Expression $value, Twig_Node_Expression $key = null) { if (null === $key) { - $key = new Twig_Node_Expression_Constant(++$this->index, $value->getLine()); + $key = new Twig_Node_Expression_Constant(++$this->index, $value->getTemplateLine()); } array_push($this->nodes, $key, $value); } - /** - * Compiles the node to PHP. - * - * @param Twig_Compiler $compiler A Twig_Compiler instance - */ public function compile(Twig_Compiler $compiler) { $compiler->raw('array('); @@ -84,3 +79,5 @@ class Twig_Node_Expression_Array extends Twig_Node_Expression $compiler->raw(')'); } } + +class_alias('Twig_Node_Expression_Array', 'Twig\Node\Expression\ArrayExpression', false); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/AssignName.php b/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/AssignName.php index 4d5dbdb90..2e6b4c7ca 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/AssignName.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/AssignName.php @@ -3,8 +3,8 @@ /* * This file is part of Twig. * - * (c) 2009 Fabien Potencier - * (c) 2009 Armin Ronacher + * (c) Fabien Potencier + * (c) Armin Ronacher * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -12,11 +12,6 @@ class Twig_Node_Expression_AssignName extends Twig_Node_Expression_Name { - /** - * Compiles the node to PHP. - * - * @param Twig_Compiler $compiler A Twig_Compiler instance - */ public function compile(Twig_Compiler $compiler) { $compiler @@ -26,3 +21,5 @@ class Twig_Node_Expression_AssignName extends Twig_Node_Expression_Name ; } } + +class_alias('Twig_Node_Expression_AssignName', 'Twig\Node\Expression\AssignNameExpression', false); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Binary.php b/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Binary.php index 5c383d155..2b545d984 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Binary.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Binary.php @@ -3,8 +3,8 @@ /* * This file is part of Twig. * - * (c) 2009 Fabien Potencier - * (c) 2009 Armin Ronacher + * (c) Fabien Potencier + * (c) Armin Ronacher * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -16,11 +16,6 @@ abstract class Twig_Node_Expression_Binary extends Twig_Node_Expression parent::__construct(array('left' => $left, 'right' => $right), array(), $lineno); } - /** - * Compiles the node to PHP. - * - * @param Twig_Compiler $compiler A Twig_Compiler instance - */ public function compile(Twig_Compiler $compiler) { $compiler @@ -38,3 +33,5 @@ abstract class Twig_Node_Expression_Binary extends Twig_Node_Expression abstract public function operator(Twig_Compiler $compiler); } + +class_alias('Twig_Node_Expression_Binary', 'Twig\Node\Expression\Binary\AbstractBinary', false); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Binary/Add.php b/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Binary/Add.php index 0ef8e1172..5a09d8367 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Binary/Add.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Binary/Add.php @@ -3,8 +3,8 @@ /* * This file is part of Twig. * - * (c) 2009 Fabien Potencier - * (c) 2009 Armin Ronacher + * (c) Fabien Potencier + * (c) Armin Ronacher * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -16,3 +16,5 @@ class Twig_Node_Expression_Binary_Add extends Twig_Node_Expression_Binary return $compiler->raw('+'); } } + +class_alias('Twig_Node_Expression_Binary_Add', 'Twig\Node\Expression\Binary\AddBinary', false); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Binary/And.php b/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Binary/And.php index d5752ebba..9ffddce6b 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Binary/And.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Binary/And.php @@ -3,8 +3,8 @@ /* * This file is part of Twig. * - * (c) 2009 Fabien Potencier - * (c) 2009 Armin Ronacher + * (c) Fabien Potencier + * (c) Armin Ronacher * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -16,3 +16,5 @@ class Twig_Node_Expression_Binary_And extends Twig_Node_Expression_Binary return $compiler->raw('&&'); } } + +class_alias('Twig_Node_Expression_Binary_And', 'Twig\Node\Expression\Binary\AndBinary', false); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Binary/BitwiseAnd.php b/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Binary/BitwiseAnd.php index 9a46d8455..e46e9ebfa 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Binary/BitwiseAnd.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Binary/BitwiseAnd.php @@ -3,8 +3,8 @@ /* * This file is part of Twig. * - * (c) 2009 Fabien Potencier - * (c) 2009 Armin Ronacher + * (c) Fabien Potencier + * (c) Armin Ronacher * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -16,3 +16,5 @@ class Twig_Node_Expression_Binary_BitwiseAnd extends Twig_Node_Expression_Binary return $compiler->raw('&'); } } + +class_alias('Twig_Node_Expression_Binary_BitwiseAnd', 'Twig\Node\Expression\Binary\BitwiseAndBinary', false); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Binary/BitwiseOr.php b/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Binary/BitwiseOr.php index 058a20bf6..5d7f1b7c0 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Binary/BitwiseOr.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Binary/BitwiseOr.php @@ -3,8 +3,8 @@ /* * This file is part of Twig. * - * (c) 2009 Fabien Potencier - * (c) 2009 Armin Ronacher + * (c) Fabien Potencier + * (c) Armin Ronacher * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -16,3 +16,5 @@ class Twig_Node_Expression_Binary_BitwiseOr extends Twig_Node_Expression_Binary return $compiler->raw('|'); } } + +class_alias('Twig_Node_Expression_Binary_BitwiseOr', 'Twig\Node\Expression\Binary\BitwiseOrBinary', false); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Binary/BitwiseXor.php b/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Binary/BitwiseXor.php index f4da73d44..82edf516c 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Binary/BitwiseXor.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Binary/BitwiseXor.php @@ -3,8 +3,8 @@ /* * This file is part of Twig. * - * (c) 2009 Fabien Potencier - * (c) 2009 Armin Ronacher + * (c) Fabien Potencier + * (c) Armin Ronacher * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -16,3 +16,5 @@ class Twig_Node_Expression_Binary_BitwiseXor extends Twig_Node_Expression_Binary return $compiler->raw('^'); } } + +class_alias('Twig_Node_Expression_Binary_BitwiseXor', 'Twig\Node\Expression\Binary\BitwiseXorBinary', false); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Binary/Concat.php b/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Binary/Concat.php index f9a646270..91abca602 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Binary/Concat.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Binary/Concat.php @@ -3,8 +3,8 @@ /* * This file is part of Twig. * - * (c) 2009 Fabien Potencier - * (c) 2009 Armin Ronacher + * (c) Fabien Potencier + * (c) Armin Ronacher * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -16,3 +16,5 @@ class Twig_Node_Expression_Binary_Concat extends Twig_Node_Expression_Binary return $compiler->raw('.'); } } + +class_alias('Twig_Node_Expression_Binary_Concat', 'Twig\Node\Expression\Binary\ConcatBinary', false); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Binary/Div.php b/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Binary/Div.php index e0797a612..38ffa30c3 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Binary/Div.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Binary/Div.php @@ -3,8 +3,8 @@ /* * This file is part of Twig. * - * (c) 2009 Fabien Potencier - * (c) 2009 Armin Ronacher + * (c) Fabien Potencier + * (c) Armin Ronacher * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -16,3 +16,5 @@ class Twig_Node_Expression_Binary_Div extends Twig_Node_Expression_Binary return $compiler->raw('/'); } } + +class_alias('Twig_Node_Expression_Binary_Div', 'Twig\Node\Expression\Binary\DivBinary', false); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Binary/EndsWith.php b/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Binary/EndsWith.php index 93b3b96f0..85c529376 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Binary/EndsWith.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Binary/EndsWith.php @@ -3,7 +3,7 @@ /* * This file is part of Twig. * - * (c) 2013 Fabien Potencier + * (c) Fabien Potencier * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -28,3 +28,5 @@ class Twig_Node_Expression_Binary_EndsWith extends Twig_Node_Expression_Binary return $compiler->raw(''); } } + +class_alias('Twig_Node_Expression_Binary_EndsWith', 'Twig\Node\Expression\Binary\EndsWithBinary', false); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Binary/Equal.php b/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Binary/Equal.php index 7b1236d0a..a6a6946b6 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Binary/Equal.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Binary/Equal.php @@ -3,7 +3,7 @@ /* * This file is part of Twig. * - * (c) 2010 Fabien Potencier + * (c) Fabien Potencier * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -15,3 +15,5 @@ class Twig_Node_Expression_Binary_Equal extends Twig_Node_Expression_Binary return $compiler->raw('=='); } } + +class_alias('Twig_Node_Expression_Binary_Equal', 'Twig\Node\Expression\Binary\EqualBinary', false); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Binary/FloorDiv.php b/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Binary/FloorDiv.php index d3518b558..7393bcb89 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Binary/FloorDiv.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Binary/FloorDiv.php @@ -3,23 +3,18 @@ /* * This file is part of Twig. * - * (c) 2009 Fabien Potencier + * (c) Fabien Potencier * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ class Twig_Node_Expression_Binary_FloorDiv extends Twig_Node_Expression_Binary { - /** - * Compiles the node to PHP. - * - * @param Twig_Compiler $compiler A Twig_Compiler instance - */ public function compile(Twig_Compiler $compiler) { - $compiler->raw('intval(floor('); + $compiler->raw('(int) floor('); parent::compile($compiler); - $compiler->raw('))'); + $compiler->raw(')'); } public function operator(Twig_Compiler $compiler) @@ -27,3 +22,5 @@ class Twig_Node_Expression_Binary_FloorDiv extends Twig_Node_Expression_Binary return $compiler->raw('/'); } } + +class_alias('Twig_Node_Expression_Binary_FloorDiv', 'Twig\Node\Expression\Binary\FloorDivBinary', false); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Binary/Greater.php b/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Binary/Greater.php index a110bd92d..832f97977 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Binary/Greater.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Binary/Greater.php @@ -3,7 +3,7 @@ /* * This file is part of Twig. * - * (c) 2010 Fabien Potencier + * (c) Fabien Potencier * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -15,3 +15,5 @@ class Twig_Node_Expression_Binary_Greater extends Twig_Node_Expression_Binary return $compiler->raw('>'); } } + +class_alias('Twig_Node_Expression_Binary_Greater', 'Twig\Node\Expression\Binary\GreaterBinary', false); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Binary/GreaterEqual.php b/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Binary/GreaterEqual.php index 3754fed21..c5f762457 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Binary/GreaterEqual.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Binary/GreaterEqual.php @@ -3,7 +3,7 @@ /* * This file is part of Twig. * - * (c) 2010 Fabien Potencier + * (c) Fabien Potencier * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -15,3 +15,5 @@ class Twig_Node_Expression_Binary_GreaterEqual extends Twig_Node_Expression_Bina return $compiler->raw('>='); } } + +class_alias('Twig_Node_Expression_Binary_GreaterEqual', 'Twig\Node\Expression\Binary\GreaterEqualBinary', false); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Binary/In.php b/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Binary/In.php index 1d485b61c..af112448f 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Binary/In.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Binary/In.php @@ -3,18 +3,13 @@ /* * This file is part of Twig. * - * (c) 2010 Fabien Potencier + * (c) Fabien Potencier * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ class Twig_Node_Expression_Binary_In extends Twig_Node_Expression_Binary { - /** - * Compiles the node to PHP. - * - * @param Twig_Compiler $compiler A Twig_Compiler instance - */ public function compile(Twig_Compiler $compiler) { $compiler @@ -31,3 +26,5 @@ class Twig_Node_Expression_Binary_In extends Twig_Node_Expression_Binary return $compiler->raw('in'); } } + +class_alias('Twig_Node_Expression_Binary_In', 'Twig\Node\Expression\Binary\InBinary', false); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Binary/Less.php b/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Binary/Less.php index 45fd30049..ab8fc1f9a 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Binary/Less.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Binary/Less.php @@ -3,7 +3,7 @@ /* * This file is part of Twig. * - * (c) 2010 Fabien Potencier + * (c) Fabien Potencier * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -15,3 +15,5 @@ class Twig_Node_Expression_Binary_Less extends Twig_Node_Expression_Binary return $compiler->raw('<'); } } + +class_alias('Twig_Node_Expression_Binary_Less', 'Twig\Node\Expression\Binary\LessBinary', false); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Binary/LessEqual.php b/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Binary/LessEqual.php index e38e257c3..71a279e9c 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Binary/LessEqual.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Binary/LessEqual.php @@ -3,7 +3,7 @@ /* * This file is part of Twig. * - * (c) 2010 Fabien Potencier + * (c) Fabien Potencier * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -15,3 +15,5 @@ class Twig_Node_Expression_Binary_LessEqual extends Twig_Node_Expression_Binary return $compiler->raw('<='); } } + +class_alias('Twig_Node_Expression_Binary_LessEqual', 'Twig\Node\Expression\Binary\LessEqualBinary', false); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Binary/Matches.php b/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Binary/Matches.php index 93bb29205..5cb855842 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Binary/Matches.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Binary/Matches.php @@ -3,7 +3,7 @@ /* * This file is part of Twig. * - * (c) 2013 Fabien Potencier + * (c) Fabien Potencier * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -26,3 +26,5 @@ class Twig_Node_Expression_Binary_Matches extends Twig_Node_Expression_Binary return $compiler->raw(''); } } + +class_alias('Twig_Node_Expression_Binary_Matches', 'Twig\Node\Expression\Binary\MatchesBinary', false); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Binary/Mod.php b/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Binary/Mod.php index 9924114fb..28109633d 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Binary/Mod.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Binary/Mod.php @@ -3,8 +3,8 @@ /* * This file is part of Twig. * - * (c) 2009 Fabien Potencier - * (c) 2009 Armin Ronacher + * (c) Fabien Potencier + * (c) Armin Ronacher * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -16,3 +16,5 @@ class Twig_Node_Expression_Binary_Mod extends Twig_Node_Expression_Binary return $compiler->raw('%'); } } + +class_alias('Twig_Node_Expression_Binary_Mod', 'Twig\Node\Expression\Binary\ModBinary', false); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Binary/Mul.php b/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Binary/Mul.php index c91529ca6..790c6a22d 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Binary/Mul.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Binary/Mul.php @@ -3,8 +3,8 @@ /* * This file is part of Twig. * - * (c) 2009 Fabien Potencier - * (c) 2009 Armin Ronacher + * (c) Fabien Potencier + * (c) Armin Ronacher * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -16,3 +16,5 @@ class Twig_Node_Expression_Binary_Mul extends Twig_Node_Expression_Binary return $compiler->raw('*'); } } + +class_alias('Twig_Node_Expression_Binary_Mul', 'Twig\Node\Expression\Binary\MulBinary', false); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Binary/NotEqual.php b/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Binary/NotEqual.php index 26867ba20..bb45c9ed5 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Binary/NotEqual.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Binary/NotEqual.php @@ -3,7 +3,7 @@ /* * This file is part of Twig. * - * (c) 2010 Fabien Potencier + * (c) Fabien Potencier * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -15,3 +15,5 @@ class Twig_Node_Expression_Binary_NotEqual extends Twig_Node_Expression_Binary return $compiler->raw('!='); } } + +class_alias('Twig_Node_Expression_Binary_NotEqual', 'Twig\Node\Expression\Binary\NotEqualBinary', false); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Binary/NotIn.php b/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Binary/NotIn.php index 8f215f1c1..9dedf92f9 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Binary/NotIn.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Binary/NotIn.php @@ -3,18 +3,13 @@ /* * This file is part of Twig. * - * (c) 2010 Fabien Potencier + * (c) Fabien Potencier * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ class Twig_Node_Expression_Binary_NotIn extends Twig_Node_Expression_Binary { - /** - * Compiles the node to PHP. - * - * @param Twig_Compiler $compiler A Twig_Compiler instance - */ public function compile(Twig_Compiler $compiler) { $compiler @@ -31,3 +26,5 @@ class Twig_Node_Expression_Binary_NotIn extends Twig_Node_Expression_Binary return $compiler->raw('not in'); } } + +class_alias('Twig_Node_Expression_Binary_NotIn', 'Twig\Node\Expression\Binary\NotInBinary', false); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Binary/Or.php b/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Binary/Or.php index adba49c6f..dc9eece14 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Binary/Or.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Binary/Or.php @@ -3,8 +3,8 @@ /* * This file is part of Twig. * - * (c) 2009 Fabien Potencier - * (c) 2009 Armin Ronacher + * (c) Fabien Potencier + * (c) Armin Ronacher * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -16,3 +16,5 @@ class Twig_Node_Expression_Binary_Or extends Twig_Node_Expression_Binary return $compiler->raw('||'); } } + +class_alias('Twig_Node_Expression_Binary_Or', 'Twig\Node\Expression\Binary\OrBinary', false); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Binary/Power.php b/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Binary/Power.php index 6cd3a217c..d24777bd5 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Binary/Power.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Binary/Power.php @@ -3,20 +3,19 @@ /* * This file is part of Twig. * - * (c) 2010 Fabien Potencier + * (c) Fabien Potencier * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ class Twig_Node_Expression_Binary_Power extends Twig_Node_Expression_Binary { - /** - * Compiles the node to PHP. - * - * @param Twig_Compiler $compiler A Twig_Compiler instance - */ public function compile(Twig_Compiler $compiler) { + if (PHP_VERSION_ID >= 50600) { + return parent::compile($compiler); + } + $compiler ->raw('pow(') ->subcompile($this->getNode('left')) @@ -31,3 +30,5 @@ class Twig_Node_Expression_Binary_Power extends Twig_Node_Expression_Binary return $compiler->raw('**'); } } + +class_alias('Twig_Node_Expression_Binary_Power', 'Twig\Node\Expression\Binary\PowerBinary', false); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Binary/Range.php b/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Binary/Range.php index fc102fed3..187f67653 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Binary/Range.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Binary/Range.php @@ -3,18 +3,13 @@ /* * This file is part of Twig. * - * (c) 2010 Fabien Potencier + * (c) Fabien Potencier * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ class Twig_Node_Expression_Binary_Range extends Twig_Node_Expression_Binary { - /** - * Compiles the node to PHP. - * - * @param Twig_Compiler $compiler A Twig_Compiler instance - */ public function compile(Twig_Compiler $compiler) { $compiler @@ -31,3 +26,5 @@ class Twig_Node_Expression_Binary_Range extends Twig_Node_Expression_Binary return $compiler->raw('..'); } } + +class_alias('Twig_Node_Expression_Binary_Range', 'Twig\Node\Expression\Binary\RangeBinary', false); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Binary/StartsWith.php b/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Binary/StartsWith.php index d2e30d66e..7e43b8de0 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Binary/StartsWith.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Binary/StartsWith.php @@ -3,7 +3,7 @@ /* * This file is part of Twig. * - * (c) 2013 Fabien Potencier + * (c) Fabien Potencier * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -28,3 +28,5 @@ class Twig_Node_Expression_Binary_StartsWith extends Twig_Node_Expression_Binary return $compiler->raw(''); } } + +class_alias('Twig_Node_Expression_Binary_StartsWith', 'Twig\Node\Expression\Binary\StartsWithBinary', false); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Binary/Sub.php b/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Binary/Sub.php index d44639915..cff8ed075 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Binary/Sub.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Binary/Sub.php @@ -3,8 +3,8 @@ /* * This file is part of Twig. * - * (c) 2009 Fabien Potencier - * (c) 2009 Armin Ronacher + * (c) Fabien Potencier + * (c) Armin Ronacher * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -16,3 +16,5 @@ class Twig_Node_Expression_Binary_Sub extends Twig_Node_Expression_Binary return $compiler->raw('-'); } } + +class_alias('Twig_Node_Expression_Binary_Sub', 'Twig\Node\Expression\Binary\SubBinary', false); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/BlockReference.php b/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/BlockReference.php index c25aadd92..37a3983d6 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/BlockReference.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/BlockReference.php @@ -3,8 +3,8 @@ /* * This file is part of Twig. * - * (c) 2009 Fabien Potencier - * (c) 2009 Armin Ronacher + * (c) Fabien Potencier + * (c) Armin Ronacher * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -17,35 +17,77 @@ */ class Twig_Node_Expression_BlockReference extends Twig_Node_Expression { - public function __construct(Twig_NodeInterface $name, $asString = false, $lineno, $tag = null) + /** + * @param Twig_Node|null $template + */ + public function __construct(Twig_NodeInterface $name, $template = null, $lineno, $tag = null) { - parent::__construct(array('name' => $name), array('as_string' => $asString, 'output' => false), $lineno, $tag); + if (is_bool($template)) { + @trigger_error(sprintf('The %s method "$asString" argument is deprecated since version 1.28 and will be removed in 2.0.', __METHOD__), E_USER_DEPRECATED); + + $template = null; + } + + $nodes = array('name' => $name); + if (null !== $template) { + $nodes['template'] = $template; + } + + parent::__construct($nodes, array('is_defined_test' => false, 'output' => false), $lineno, $tag); } - /** - * Compiles the node to PHP. - * - * @param Twig_Compiler $compiler A Twig_Compiler instance - */ public function compile(Twig_Compiler $compiler) { - if ($this->getAttribute('as_string')) { - $compiler->raw('(string) '); - } - - if ($this->getAttribute('output')) { - $compiler - ->addDebugInfo($this) - ->write('$this->displayBlock(') - ->subcompile($this->getNode('name')) - ->raw(", \$context, \$blocks);\n") - ; + if ($this->getAttribute('is_defined_test')) { + $this->compileTemplateCall($compiler, 'hasBlock'); } else { - $compiler - ->raw('$this->renderBlock(') - ->subcompile($this->getNode('name')) - ->raw(', $context, $blocks)') - ; + if ($this->getAttribute('output')) { + $compiler->addDebugInfo($this); + + $this + ->compileTemplateCall($compiler, 'displayBlock') + ->raw(";\n"); + } else { + $this->compileTemplateCall($compiler, 'renderBlock'); + } } } + + private function compileTemplateCall(Twig_Compiler $compiler, $method) + { + if (!$this->hasNode('template')) { + $compiler->write('$this'); + } else { + $compiler + ->write('$this->loadTemplate(') + ->subcompile($this->getNode('template')) + ->raw(', ') + ->repr($this->getTemplateName()) + ->raw(', ') + ->repr($this->getTemplateLine()) + ->raw(')') + ; + } + + $compiler->raw(sprintf('->%s', $method)); + $this->compileBlockArguments($compiler); + + return $compiler; + } + + private function compileBlockArguments(Twig_Compiler $compiler) + { + $compiler + ->raw('(') + ->subcompile($this->getNode('name')) + ->raw(', $context'); + + if (!$this->hasNode('template')) { + $compiler->raw(', $blocks'); + } + + return $compiler->raw(')'); + } } + +class_alias('Twig_Node_Expression_BlockReference', 'Twig\Node\Expression\BlockReferenceExpression', false); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Call.php b/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Call.php index 51e2cac5b..d962b6a50 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Call.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Call.php @@ -3,25 +3,36 @@ /* * This file is part of Twig. * - * (c) 2012 Fabien Potencier + * (c) Fabien Potencier * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ abstract class Twig_Node_Expression_Call extends Twig_Node_Expression { + private $reflector; + protected function compileCallable(Twig_Compiler $compiler) { $closingParenthesis = false; if ($this->hasAttribute('callable') && $callable = $this->getAttribute('callable')) { - if (is_string($callable)) { + if (is_string($callable) && false === strpos($callable, '::')) { $compiler->raw($callable); - } elseif (is_array($callable) && $callable[0] instanceof Twig_ExtensionInterface) { - $compiler->raw(sprintf('$this->env->getExtension(\'%s\')->%s', $callable[0]->getName(), $callable[1])); } else { - $type = ucfirst($this->getAttribute('type')); - $compiler->raw(sprintf('call_user_func_array($this->env->get%s(\'%s\')->getCallable(), array', $type, $this->getAttribute('name'))); - $closingParenthesis = true; + list($r, $callable) = $this->reflectCallable($callable); + if ($r instanceof ReflectionMethod && is_string($callable[0])) { + if ($r->isStatic()) { + $compiler->raw(sprintf('%s::%s', $callable[0], $callable[1])); + } else { + $compiler->raw(sprintf('$this->env->getRuntime(\'%s\')->%s', $callable[0], $callable[1])); + } + } elseif ($r instanceof ReflectionMethod && $callable[0] instanceof Twig_ExtensionInterface) { + $compiler->raw(sprintf('$this->env->getExtension(\'%s\')->%s', get_class($callable[0]), $callable[1])); + } else { + $type = ucfirst($this->getAttribute('type')); + $compiler->raw(sprintf('call_user_func_array($this->env->get%s(\'%s\')->getCallable(), array', $type, $this->getAttribute('name'))); + $closingParenthesis = true; + } } } else { $compiler->raw($this->getAttribute('thing')->compile()); @@ -71,7 +82,7 @@ abstract class Twig_Node_Expression_Call extends Twig_Node_Expression $first = false; } - if ($this->hasNode('arguments') && null !== $this->getNode('arguments')) { + if ($this->hasNode('arguments')) { $callable = $this->hasAttribute('callable') ? $this->getAttribute('callable') : null; $arguments = $this->getArguments($callable, $this->getNode('arguments')); @@ -121,61 +132,21 @@ abstract class Twig_Node_Expression_Call extends Twig_Node_Expression throw new LogicException($message); } - // manage named arguments - if (is_array($callable)) { - $r = new ReflectionMethod($callable[0], $callable[1]); - } elseif (is_object($callable) && !$callable instanceof Closure) { - $r = new ReflectionObject($callable); - $r = $r->getMethod('__invoke'); - } elseif (is_string($callable) && false !== strpos($callable, '::')) { - $r = new ReflectionMethod($callable); - } else { - $r = new ReflectionFunction($callable); - } - - $definition = $r->getParameters(); - if ($this->hasNode('node')) { - array_shift($definition); - } - if ($this->hasAttribute('needs_environment') && $this->getAttribute('needs_environment')) { - array_shift($definition); - } - if ($this->hasAttribute('needs_context') && $this->getAttribute('needs_context')) { - array_shift($definition); - } - if ($this->hasAttribute('arguments') && null !== $this->getAttribute('arguments')) { - foreach ($this->getAttribute('arguments') as $argument) { - array_shift($definition); - } - } - if ($isVariadic) { - $argument = end($definition); - if ($argument && $argument->isArray() && $argument->isDefaultValueAvailable() && array() === $argument->getDefaultValue()) { - array_pop($definition); - } else { - $callableName = $r->name; - if ($r->getDeclaringClass()) { - $callableName = $r->getDeclaringClass()->name.'::'.$callableName; - } - - throw new LogicException(sprintf('The last parameter of "%s" for %s "%s" must be an array with default value, eg. "array $arg = array()".', $callableName, $callType, $callName)); - } - } - + $callableParameters = $this->getCallableParameters($callable, $isVariadic); $arguments = array(); $names = array(); $missingArguments = array(); $optionalArguments = array(); $pos = 0; - foreach ($definition as $param) { - $names[] = $name = $this->normalizeName($param->name); + foreach ($callableParameters as $callableParameter) { + $names[] = $name = $this->normalizeName($callableParameter->name); if (array_key_exists($name, $parameters)) { if (array_key_exists($pos, $parameters)) { throw new Twig_Error_Syntax(sprintf('Argument "%s" is defined twice for %s "%s".', $name, $callType, $callName)); } - if (!empty($missingArguments)) { + if (count($missingArguments)) { throw new Twig_Error_Syntax(sprintf( 'Argument "%s" could not be assigned for %s "%s(%s)" because it is mapped to an internal PHP function which cannot determine default value for optional argument%s "%s".', $name, $callType, $callName, implode(', ', $names), count($missingArguments) > 1 ? 's' : '', implode('", "', $missingArguments)) @@ -192,9 +163,9 @@ abstract class Twig_Node_Expression_Call extends Twig_Node_Expression unset($parameters[$pos]); $optionalArguments = array(); ++$pos; - } elseif ($param->isDefaultValueAvailable()) { - $optionalArguments[] = new Twig_Node_Expression_Constant($param->getDefaultValue(), -1); - } elseif ($param->isOptional()) { + } elseif ($callableParameter->isDefaultValueAvailable()) { + $optionalArguments[] = new Twig_Node_Expression_Constant($callableParameter->getDefaultValue(), -1); + } elseif ($callableParameter->isOptional()) { if (empty($parameters)) { break; } else { @@ -234,7 +205,7 @@ abstract class Twig_Node_Expression_Call extends Twig_Node_Expression throw new Twig_Error_Syntax(sprintf( 'Unknown argument%s "%s" for %s "%s(%s)".', count($parameters) > 1 ? 's' : '', implode('", "', array_keys($parameters)), $callType, $callName, implode(', ', $names) - ), $unknownParameter ? $unknownParameter->getLine() : -1); + ), $unknownParameter ? $unknownParameter->getTemplateLine() : -1); } return $arguments; @@ -244,4 +215,77 @@ abstract class Twig_Node_Expression_Call extends Twig_Node_Expression { return strtolower(preg_replace(array('/([A-Z]+)([A-Z][a-z])/', '/([a-z\d])([A-Z])/'), array('\\1_\\2', '\\1_\\2'), $name)); } + + private function getCallableParameters($callable, $isVariadic) + { + list($r) = $this->reflectCallable($callable); + if (null === $r) { + return array(); + } + + $parameters = $r->getParameters(); + if ($this->hasNode('node')) { + array_shift($parameters); + } + if ($this->hasAttribute('needs_environment') && $this->getAttribute('needs_environment')) { + array_shift($parameters); + } + if ($this->hasAttribute('needs_context') && $this->getAttribute('needs_context')) { + array_shift($parameters); + } + if ($this->hasAttribute('arguments') && null !== $this->getAttribute('arguments')) { + foreach ($this->getAttribute('arguments') as $argument) { + array_shift($parameters); + } + } + if ($isVariadic) { + $argument = end($parameters); + if ($argument && $argument->isArray() && $argument->isDefaultValueAvailable() && array() === $argument->getDefaultValue()) { + array_pop($parameters); + } else { + $callableName = $r->name; + if ($r instanceof ReflectionMethod) { + $callableName = $r->getDeclaringClass()->name.'::'.$callableName; + } + + throw new LogicException(sprintf('The last parameter of "%s" for %s "%s" must be an array with default value, eg. "array $arg = array()".', $callableName, $this->getAttribute('type'), $this->getAttribute('name'))); + } + } + + return $parameters; + } + + private function reflectCallable($callable) + { + if (null !== $this->reflector) { + return $this->reflector; + } + + if (is_array($callable)) { + if (!method_exists($callable[0], $callable[1])) { + // __call() + return array(null, array()); + } + $r = new ReflectionMethod($callable[0], $callable[1]); + } elseif (is_object($callable) && !$callable instanceof Closure) { + $r = new ReflectionObject($callable); + $r = $r->getMethod('__invoke'); + $callable = array($callable, '__invoke'); + } elseif (is_string($callable) && false !== $pos = strpos($callable, '::')) { + $class = substr($callable, 0, $pos); + $method = substr($callable, $pos + 2); + if (!method_exists($class, $method)) { + // __staticCall() + return array(null, array()); + } + $r = new ReflectionMethod($callable); + $callable = array($class, $method); + } else { + $r = new ReflectionFunction($callable); + } + + return $this->reflector = array($r, $callable); + } } + +class_alias('Twig_Node_Expression_Call', 'Twig\Node\Expression\CallExpression', false); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Conditional.php b/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Conditional.php index edcb1e2d2..c339d773c 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Conditional.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Conditional.php @@ -3,8 +3,8 @@ /* * This file is part of Twig. * - * (c) 2009 Fabien Potencier - * (c) 2009 Armin Ronacher + * (c) Fabien Potencier + * (c) Armin Ronacher * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -29,3 +29,5 @@ class Twig_Node_Expression_Conditional extends Twig_Node_Expression ; } } + +class_alias('Twig_Node_Expression_Conditional', 'Twig\Node\Expression\ConditionalExpression', false); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Constant.php b/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Constant.php index a91dc6985..bf4d031cf 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Constant.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Constant.php @@ -3,8 +3,8 @@ /* * This file is part of Twig. * - * (c) 2009 Fabien Potencier - * (c) 2009 Armin Ronacher + * (c) Fabien Potencier + * (c) Armin Ronacher * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -21,3 +21,5 @@ class Twig_Node_Expression_Constant extends Twig_Node_Expression $compiler->repr($this->getAttribute('value')); } } + +class_alias('Twig_Node_Expression_Constant', 'Twig\Node\Expression\ConstantExpression', false); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/ExtensionReference.php b/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/ExtensionReference.php index db06abb0a..114b5cd97 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/ExtensionReference.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/ExtensionReference.php @@ -3,16 +3,20 @@ /* * This file is part of Twig. * - * (c) 2009 Fabien Potencier + * (c) Fabien Potencier * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ +@trigger_error('The Twig_Node_Expression_ExtensionReference class is deprecated since version 1.23 and will be removed in 2.0.', E_USER_DEPRECATED); + /** * Represents an extension call node. * * @author Fabien Potencier + * + * @deprecated since 1.23 and will be removed in 2.0. */ class Twig_Node_Expression_ExtensionReference extends Twig_Node_Expression { @@ -21,11 +25,6 @@ class Twig_Node_Expression_ExtensionReference extends Twig_Node_Expression parent::__construct(array(), array('name' => $name), $lineno, $tag); } - /** - * Compiles the node to PHP. - * - * @param Twig_Compiler $compiler A Twig_Compiler instance - */ public function compile(Twig_Compiler $compiler) { $compiler->raw(sprintf("\$this->env->getExtension('%s')", $this->getAttribute('name'))); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Filter.php b/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Filter.php index a906232ea..12da1d676 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Filter.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Filter.php @@ -3,8 +3,8 @@ /* * This file is part of Twig. * - * (c) 2009 Fabien Potencier - * (c) 2009 Armin Ronacher + * (c) Fabien Potencier + * (c) Armin Ronacher * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -37,3 +37,5 @@ class Twig_Node_Expression_Filter extends Twig_Node_Expression_Call $this->compileCallable($compiler); } } + +class_alias('Twig_Node_Expression_Filter', 'Twig\Node\Expression\FilterExpression', false); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Filter/Default.php b/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Filter/Default.php index 1827c8886..d2e19d54e 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Filter/Default.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Filter/Default.php @@ -3,7 +3,7 @@ /* * This file is part of Twig. * - * (c) 2011 Fabien Potencier + * (c) Fabien Potencier * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -22,13 +22,13 @@ class Twig_Node_Expression_Filter_Default extends Twig_Node_Expression_Filter { public function __construct(Twig_NodeInterface $node, Twig_Node_Expression_Constant $filterName, Twig_NodeInterface $arguments, $lineno, $tag = null) { - $default = new Twig_Node_Expression_Filter($node, new Twig_Node_Expression_Constant('default', $node->getLine()), $arguments, $node->getLine()); + $default = new Twig_Node_Expression_Filter($node, new Twig_Node_Expression_Constant('default', $node->getTemplateLine()), $arguments, $node->getTemplateLine()); if ('default' === $filterName->getAttribute('value') && ($node instanceof Twig_Node_Expression_Name || $node instanceof Twig_Node_Expression_GetAttr)) { - $test = new Twig_Node_Expression_Test_Defined(clone $node, 'defined', new Twig_Node(), $node->getLine()); - $false = count($arguments) ? $arguments->getNode(0) : new Twig_Node_Expression_Constant('', $node->getLine()); + $test = new Twig_Node_Expression_Test_Defined(clone $node, 'defined', new Twig_Node(), $node->getTemplateLine()); + $false = count($arguments) ? $arguments->getNode(0) : new Twig_Node_Expression_Constant('', $node->getTemplateLine()); - $node = new Twig_Node_Expression_Conditional($test, $default, $false, $node->getLine()); + $node = new Twig_Node_Expression_Conditional($test, $default, $false, $node->getTemplateLine()); } else { $node = $default; } @@ -41,3 +41,5 @@ class Twig_Node_Expression_Filter_Default extends Twig_Node_Expression_Filter $compiler->subcompile($this->getNode('node')); } } + +class_alias('Twig_Node_Expression_Filter_Default', 'Twig\Node\Expression\Filter\DefaultFilter', false); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Function.php b/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Function.php index 7326ede26..cdee7c972 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Function.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Function.php @@ -3,7 +3,7 @@ /* * This file is part of Twig. * - * (c) 2010 Fabien Potencier + * (c) Fabien Potencier * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -12,7 +12,7 @@ class Twig_Node_Expression_Function extends Twig_Node_Expression_Call { public function __construct($name, Twig_NodeInterface $arguments, $lineno) { - parent::__construct(array('arguments' => $arguments), array('name' => $name), $lineno); + parent::__construct(array('arguments' => $arguments), array('name' => $name, 'is_defined_test' => false), $lineno); } public function compile(Twig_Compiler $compiler) @@ -27,7 +27,12 @@ class Twig_Node_Expression_Function extends Twig_Node_Expression_Call $this->setAttribute('needs_context', $function->needsContext()); $this->setAttribute('arguments', $function->getArguments()); if ($function instanceof Twig_FunctionCallableInterface || $function instanceof Twig_SimpleFunction) { - $this->setAttribute('callable', $function->getCallable()); + $callable = $function->getCallable(); + if ('constant' === $name && $this->getAttribute('is_defined_test')) { + $callable = 'twig_constant_is_defined'; + } + + $this->setAttribute('callable', $callable); } if ($function instanceof Twig_SimpleFunction) { $this->setAttribute('is_variadic', $function->isVariadic()); @@ -36,3 +41,5 @@ class Twig_Node_Expression_Function extends Twig_Node_Expression_Call $this->compileCallable($compiler); } } + +class_alias('Twig_Node_Expression_Function', 'Twig\Node\Expression\FunctionExpression', false); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/GetAttr.php b/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/GetAttr.php index 6ce61111c..b7823accb 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/GetAttr.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/GetAttr.php @@ -3,8 +3,8 @@ /* * This file is part of Twig. * - * (c) 2009 Fabien Potencier - * (c) 2009 Armin Ronacher + * (c) Fabien Potencier + * (c) Armin Ronacher * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,11 +13,20 @@ class Twig_Node_Expression_GetAttr extends Twig_Node_Expression { public function __construct(Twig_Node_Expression $node, Twig_Node_Expression $attribute, Twig_Node_Expression $arguments = null, $type, $lineno) { - parent::__construct(array('node' => $node, 'attribute' => $attribute, 'arguments' => $arguments), array('type' => $type, 'is_defined_test' => false, 'ignore_strict_check' => false, 'disable_c_ext' => false), $lineno); + $nodes = array('node' => $node, 'attribute' => $attribute); + if (null !== $arguments) { + $nodes['arguments'] = $arguments; + } + + parent::__construct($nodes, array('type' => $type, 'is_defined_test' => false, 'ignore_strict_check' => false, 'disable_c_ext' => false), $lineno); } public function compile(Twig_Compiler $compiler) { + if ($this->getAttribute('disable_c_ext')) { + @trigger_error(sprintf('Using the "disable_c_ext" attribute on %s is deprecated since version 1.30 and will be removed in 2.0.', __CLASS__), E_USER_DEPRECATED); + } + if (function_exists('twig_template_get_attributes') && !$this->getAttribute('disable_c_ext')) { $compiler->raw('twig_template_get_attributes($this, '); } else { @@ -36,10 +45,10 @@ class Twig_Node_Expression_GetAttr extends Twig_Node_Expression $needFourth = $this->getAttribute('ignore_strict_check'); $needThird = $needFourth || $this->getAttribute('is_defined_test'); $needSecond = $needThird || Twig_Template::ANY_CALL !== $this->getAttribute('type'); - $needFirst = $needSecond || null !== $this->getNode('arguments'); + $needFirst = $needSecond || $this->hasNode('arguments'); if ($needFirst) { - if (null !== $this->getNode('arguments')) { + if ($this->hasNode('arguments')) { $compiler->raw(', ')->subcompile($this->getNode('arguments')); } else { $compiler->raw(', array()'); @@ -61,3 +70,5 @@ class Twig_Node_Expression_GetAttr extends Twig_Node_Expression $compiler->raw(')'); } } + +class_alias('Twig_Node_Expression_GetAttr', 'Twig\Node\Expression\GetAttrExpression', false); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/MethodCall.php b/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/MethodCall.php index 620b02bf4..709016eb2 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/MethodCall.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/MethodCall.php @@ -3,7 +3,7 @@ /* * This file is part of Twig. * - * (c) 2012 Fabien Potencier + * (c) Fabien Potencier * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -39,3 +39,5 @@ class Twig_Node_Expression_MethodCall extends Twig_Node_Expression $compiler->raw(')'); } } + +class_alias('Twig_Node_Expression_MethodCall', 'Twig\Node\Expression\MethodCallExpression', false); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Name.php b/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Name.php index c062a213f..9d5a21f87 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Name.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Name.php @@ -3,8 +3,8 @@ /* * This file is part of Twig. * - * (c) 2009 Fabien Potencier - * (c) 2009 Armin Ronacher + * (c) Fabien Potencier + * (c) Armin Ronacher * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -30,19 +30,11 @@ class Twig_Node_Expression_Name extends Twig_Node_Expression if ($this->getAttribute('is_defined_test')) { if ($this->isSpecial()) { - if ('_self' === $name) { - @trigger_error(sprintf('Global variable "_self" is deprecated in %s at line %d', '?', $this->getLine()), E_USER_DEPRECATED); - } - $compiler->repr(true); } else { $compiler->raw('array_key_exists(')->repr($name)->raw(', $context)'); } } elseif ($this->isSpecial()) { - if ('_self' === $name) { - @trigger_error(sprintf('Global variable "_self" is deprecated in %s at line %d', '?', $this->getLine()), E_USER_DEPRECATED); - } - $compiler->raw($this->specialVars[$name]); } elseif ($this->getAttribute('always_defined')) { $compiler @@ -51,10 +43,20 @@ class Twig_Node_Expression_Name extends Twig_Node_Expression ->raw(']') ; } else { - // remove the non-PHP 5.4 version when PHP 5.3 support is dropped - // as the non-optimized version is just a workaround for slow ternary operator - // when the context has a lot of variables - if (PHP_VERSION_ID >= 50400) { + if (PHP_VERSION_ID >= 70000) { + // use PHP 7 null coalescing operator + $compiler + ->raw('($context[') + ->string($name) + ->raw('] ?? ') + ; + + if ($this->getAttribute('ignore_strict_check') || !$compiler->getEnvironment()->isStrictVariables()) { + $compiler->raw('null)'); + } else { + $compiler->raw('$this->getContext($context, ')->string($name)->raw('))'); + } + } elseif (PHP_VERSION_ID >= 50400) { // PHP 5.4 ternary operator performance was optimized $compiler ->raw('(isset($context[') @@ -96,3 +98,5 @@ class Twig_Node_Expression_Name extends Twig_Node_Expression return !$this->isSpecial() && !$this->getAttribute('is_defined_test'); } } + +class_alias('Twig_Node_Expression_Name', 'Twig\Node\Expression\NameExpression', false); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/NullCoalesce.php b/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/NullCoalesce.php new file mode 100644 index 000000000..eaafa4c91 --- /dev/null +++ b/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/NullCoalesce.php @@ -0,0 +1,48 @@ +getTemplateLine()), + new Twig_Node_Expression_Unary_Not(new Twig_Node_Expression_Test_Null($left, 'null', new Twig_Node(), $left->getTemplateLine()), $left->getTemplateLine()), + $left->getTemplateLine() + ); + + parent::__construct($test, $left, $right, $lineno); + } + + public function compile(Twig_Compiler $compiler) + { + /* + * This optimizes only one case. PHP 7 also supports more complex expressions + * that can return null. So, for instance, if log is defined, log("foo") ?? "..." works, + * but log($a["foo"]) ?? "..." does not if $a["foo"] is not defined. More advanced + * cases might be implemented as an optimizer node visitor, but has not been done + * as benefits are probably not worth the added complexity. + */ + if (PHP_VERSION_ID >= 70000 && $this->getNode('expr2') instanceof Twig_Node_Expression_Name) { + $this->getNode('expr2')->setAttribute('always_defined', true); + $compiler + ->raw('((') + ->subcompile($this->getNode('expr2')) + ->raw(') ?? (') + ->subcompile($this->getNode('expr3')) + ->raw('))') + ; + } else { + parent::compile($compiler); + } + } +} + +class_alias('Twig_Node_Expression_NullCoalesce', 'Twig\Node\Expression\NullCoalesceExpression', false); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Parent.php b/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Parent.php index bd5024b08..78692db29 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Parent.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Parent.php @@ -3,8 +3,8 @@ /* * This file is part of Twig. * - * (c) 2009 Fabien Potencier - * (c) 2009 Armin Ronacher + * (c) Fabien Potencier + * (c) Armin Ronacher * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -22,11 +22,6 @@ class Twig_Node_Expression_Parent extends Twig_Node_Expression parent::__construct(array(), array('output' => false, 'name' => $name), $lineno, $tag); } - /** - * Compiles the node to PHP. - * - * @param Twig_Compiler $compiler A Twig_Compiler instance - */ public function compile(Twig_Compiler $compiler) { if ($this->getAttribute('output')) { @@ -45,3 +40,5 @@ class Twig_Node_Expression_Parent extends Twig_Node_Expression } } } + +class_alias('Twig_Node_Expression_Parent', 'Twig\Node\Expression\ParentExpression', false); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/TempName.php b/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/TempName.php index e6b058e85..0a86e003d 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/TempName.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/TempName.php @@ -3,7 +3,7 @@ /* * This file is part of Twig. * - * (c) 2011 Fabien Potencier + * (c) Fabien Potencier * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -24,3 +24,5 @@ class Twig_Node_Expression_TempName extends Twig_Node_Expression ; } } + +class_alias('Twig_Node_Expression_TempName', 'Twig\Node\Expression\TempNameExpression', false); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Test.php b/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Test.php index c0358c8bf..ad102ba60 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Test.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Test.php @@ -3,7 +3,7 @@ /* * This file is part of Twig. * - * (c) 2010 Fabien Potencier + * (c) Fabien Potencier * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -12,7 +12,12 @@ class Twig_Node_Expression_Test extends Twig_Node_Expression_Call { public function __construct(Twig_NodeInterface $node, $name, Twig_NodeInterface $arguments = null, $lineno) { - parent::__construct(array('node' => $node, 'arguments' => $arguments), array('name' => $name), $lineno); + $nodes = array('node' => $node); + if (null !== $arguments) { + $nodes['arguments'] = $arguments; + } + + parent::__construct($nodes, array('name' => $name), $lineno); } public function compile(Twig_Compiler $compiler) @@ -33,3 +38,5 @@ class Twig_Node_Expression_Test extends Twig_Node_Expression_Call $this->compileCallable($compiler); } } + +class_alias('Twig_Node_Expression_Test', 'Twig\Node\Expression\TestExpression', false); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Test/Constant.php b/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Test/Constant.php index de55f5f55..a51a4ba13 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Test/Constant.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Test/Constant.php @@ -3,7 +3,7 @@ /* * This file is part of Twig. * - * (c) 2011 Fabien Potencier + * (c) Fabien Potencier * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -44,3 +44,5 @@ class Twig_Node_Expression_Test_Constant extends Twig_Node_Expression_Test ; } } + +class_alias('Twig_Node_Expression_Test_Constant', 'Twig\Node\Expression\Test\ConstantTest', false); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Test/Defined.php b/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Test/Defined.php index 247b2e23a..2136c3907 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Test/Defined.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Test/Defined.php @@ -3,7 +3,7 @@ /* * This file is part of Twig. * - * (c) 2011 Fabien Potencier + * (c) Fabien Potencier * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -25,17 +25,22 @@ class Twig_Node_Expression_Test_Defined extends Twig_Node_Expression_Test { public function __construct(Twig_NodeInterface $node, $name, Twig_NodeInterface $arguments = null, $lineno) { - parent::__construct($node, $name, $arguments, $lineno); - if ($node instanceof Twig_Node_Expression_Name) { $node->setAttribute('is_defined_test', true); } elseif ($node instanceof Twig_Node_Expression_GetAttr) { $node->setAttribute('is_defined_test', true); - $this->changeIgnoreStrictCheck($node); + } elseif ($node instanceof Twig_Node_Expression_BlockReference) { + $node->setAttribute('is_defined_test', true); + } elseif ($node instanceof Twig_Node_Expression_Function && 'constant' === $node->getAttribute('name')) { + $node->setAttribute('is_defined_test', true); + } elseif ($node instanceof Twig_Node_Expression_Constant || $node instanceof Twig_Node_Expression_Array) { + $node = new Twig_Node_Expression_Constant(true, $node->getTemplateLine()); } else { - throw new Twig_Error_Syntax('The "defined" test only works with simple variables', $this->getLine()); + throw new Twig_Error_Syntax('The "defined" test only works with simple variables.', $this->getTemplateLine()); } + + parent::__construct($node, $name, $arguments, $lineno); } protected function changeIgnoreStrictCheck(Twig_Node_Expression_GetAttr $node) @@ -52,3 +57,5 @@ class Twig_Node_Expression_Test_Defined extends Twig_Node_Expression_Test $compiler->subcompile($this->getNode('node')); } } + +class_alias('Twig_Node_Expression_Test_Defined', 'Twig\Node\Expression\Test\DefinedTest', false); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Test/Divisibleby.php b/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Test/Divisibleby.php index d5bed234a..a5d71961a 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Test/Divisibleby.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Test/Divisibleby.php @@ -3,7 +3,7 @@ /* * This file is part of Twig. * - * (c) 2011 Fabien Potencier + * (c) Fabien Potencier * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -31,3 +31,5 @@ class Twig_Node_Expression_Test_Divisibleby extends Twig_Node_Expression_Test ; } } + +class_alias('Twig_Node_Expression_Test_Divisibleby', 'Twig\Node\Expression\Test\DivisiblebyTest', false); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Test/Even.php b/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Test/Even.php index d7853e89c..7e198bea6 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Test/Even.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Test/Even.php @@ -3,7 +3,7 @@ /* * This file is part of Twig. * - * (c) 2011 Fabien Potencier + * (c) Fabien Potencier * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -30,3 +30,5 @@ class Twig_Node_Expression_Test_Even extends Twig_Node_Expression_Test ; } } + +class_alias('Twig_Node_Expression_Test_Even', 'Twig\Node\Expression\Test\EvenTest', false); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Test/Null.php b/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Test/Null.php index 1c83825a0..3746e4cbd 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Test/Null.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Test/Null.php @@ -3,7 +3,7 @@ /* * This file is part of Twig. * - * (c) 2011 Fabien Potencier + * (c) Fabien Potencier * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -29,3 +29,5 @@ class Twig_Node_Expression_Test_Null extends Twig_Node_Expression_Test ; } } + +class_alias('Twig_Node_Expression_Test_Null', 'Twig\Node\Expression\Test\NullTest', false); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Test/Odd.php b/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Test/Odd.php index 421c19e84..0c6c099bb 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Test/Odd.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Test/Odd.php @@ -3,7 +3,7 @@ /* * This file is part of Twig. * - * (c) 2011 Fabien Potencier + * (c) Fabien Potencier * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -30,3 +30,5 @@ class Twig_Node_Expression_Test_Odd extends Twig_Node_Expression_Test ; } } + +class_alias('Twig_Node_Expression_Test_Odd', 'Twig\Node\Expression\Test\OddTest', false); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Test/Sameas.php b/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Test/Sameas.php index b48905ee4..e95ff1f20 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Test/Sameas.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Test/Sameas.php @@ -3,7 +3,7 @@ /* * This file is part of Twig. * - * (c) 2011 Fabien Potencier + * (c) Fabien Potencier * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -27,3 +27,5 @@ class Twig_Node_Expression_Test_Sameas extends Twig_Node_Expression_Test ; } } + +class_alias('Twig_Node_Expression_Test_Sameas', 'Twig\Node\Expression\Test\SameasTest', false); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Unary.php b/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Unary.php index 1cf54c326..5804485e3 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Unary.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Unary.php @@ -3,8 +3,8 @@ /* * This file is part of Twig. * - * (c) 2009 Fabien Potencier - * (c) 2009 Armin Ronacher + * (c) Fabien Potencier + * (c) Armin Ronacher * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -25,3 +25,5 @@ abstract class Twig_Node_Expression_Unary extends Twig_Node_Expression abstract public function operator(Twig_Compiler $compiler); } + +class_alias('Twig_Node_Expression_Unary', 'Twig\Node\Expression\Unary\AbstractUnary', false); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Unary/Neg.php b/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Unary/Neg.php index 2a3937ec3..039d933d9 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Unary/Neg.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Unary/Neg.php @@ -3,8 +3,8 @@ /* * This file is part of Twig. * - * (c) 2009 Fabien Potencier - * (c) 2009 Armin Ronacher + * (c) Fabien Potencier + * (c) Armin Ronacher * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -16,3 +16,5 @@ class Twig_Node_Expression_Unary_Neg extends Twig_Node_Expression_Unary $compiler->raw('-'); } } + +class_alias('Twig_Node_Expression_Unary_Neg', 'Twig\Node\Expression\Unary\NegUnary', false); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Unary/Not.php b/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Unary/Not.php index f94073cfa..a0860b183 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Unary/Not.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Unary/Not.php @@ -3,8 +3,8 @@ /* * This file is part of Twig. * - * (c) 2009 Fabien Potencier - * (c) 2009 Armin Ronacher + * (c) Fabien Potencier + * (c) Armin Ronacher * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -16,3 +16,5 @@ class Twig_Node_Expression_Unary_Not extends Twig_Node_Expression_Unary $compiler->raw('!'); } } + +class_alias('Twig_Node_Expression_Unary_Not', 'Twig\Node\Expression\Unary\NotUnary', false); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Unary/Pos.php b/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Unary/Pos.php index 04edb52a6..eeff5452f 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Unary/Pos.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/Node/Expression/Unary/Pos.php @@ -3,8 +3,8 @@ /* * This file is part of Twig. * - * (c) 2009 Fabien Potencier - * (c) 2009 Armin Ronacher + * (c) Fabien Potencier + * (c) Armin Ronacher * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -16,3 +16,5 @@ class Twig_Node_Expression_Unary_Pos extends Twig_Node_Expression_Unary $compiler->raw('+'); } } + +class_alias('Twig_Node_Expression_Unary_Pos', 'Twig\Node\Expression\Unary\PosUnary', false); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/Node/Flush.php b/lib/silex/vendor/twig/twig/lib/Twig/Node/Flush.php index 20d6aab43..fcc461ac0 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/Node/Flush.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/Node/Flush.php @@ -3,7 +3,7 @@ /* * This file is part of Twig. * - * (c) 2011 Fabien Potencier + * (c) Fabien Potencier * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -21,11 +21,6 @@ class Twig_Node_Flush extends Twig_Node parent::__construct(array(), array(), $lineno, $tag); } - /** - * Compiles the node to PHP. - * - * @param Twig_Compiler $compiler A Twig_Compiler instance - */ public function compile(Twig_Compiler $compiler) { $compiler @@ -34,3 +29,5 @@ class Twig_Node_Flush extends Twig_Node ; } } + +class_alias('Twig_Node_Flush', 'Twig\Node\FlushNode', false); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/Node/For.php b/lib/silex/vendor/twig/twig/lib/Twig/Node/For.php index 36e9de8d3..914b70c98 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/Node/For.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/Node/For.php @@ -3,8 +3,8 @@ /* * This file is part of Twig. * - * (c) 2009 Fabien Potencier - * (c) 2009 Armin Ronacher + * (c) Fabien Potencier + * (c) Armin Ronacher * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -27,26 +27,25 @@ class Twig_Node_For extends Twig_Node $body = new Twig_Node_If(new Twig_Node(array($ifexpr, $body)), null, $lineno, $tag); } - parent::__construct(array('key_target' => $keyTarget, 'value_target' => $valueTarget, 'seq' => $seq, 'body' => $body, 'else' => $else), array('with_loop' => true, 'ifexpr' => null !== $ifexpr), $lineno, $tag); + $nodes = array('key_target' => $keyTarget, 'value_target' => $valueTarget, 'seq' => $seq, 'body' => $body); + if (null !== $else) { + $nodes['else'] = $else; + } + + parent::__construct($nodes, array('with_loop' => true, 'ifexpr' => null !== $ifexpr), $lineno, $tag); } - /** - * Compiles the node to PHP. - * - * @param Twig_Compiler $compiler A Twig_Compiler instance - */ public function compile(Twig_Compiler $compiler) { $compiler ->addDebugInfo($this) - // the (array) cast bypasses a PHP 5.2.6 bug - ->write("\$context['_parent'] = (array) \$context;\n") + ->write("\$context['_parent'] = \$context;\n") ->write("\$context['_seq'] = twig_ensure_traversable(") ->subcompile($this->getNode('seq')) ->raw(");\n") ; - if (null !== $this->getNode('else')) { + if ($this->hasNode('else')) { $compiler->write("\$context['_iterated'] = false;\n"); } @@ -75,7 +74,7 @@ class Twig_Node_For extends Twig_Node } } - $this->loop->setAttribute('else', null !== $this->getNode('else')); + $this->loop->setAttribute('else', $this->hasNode('else')); $this->loop->setAttribute('with_loop', $this->getAttribute('with_loop')); $this->loop->setAttribute('ifexpr', $this->getAttribute('ifexpr')); @@ -91,7 +90,7 @@ class Twig_Node_For extends Twig_Node ->write("}\n") ; - if (null !== $this->getNode('else')) { + if ($this->hasNode('else')) { $compiler ->write("if (!\$context['_iterated']) {\n") ->indent() @@ -110,3 +109,5 @@ class Twig_Node_For extends Twig_Node $compiler->write("\$context = array_intersect_key(\$context, \$_parent) + \$_parent;\n"); } } + +class_alias('Twig_Node_For', 'Twig\Node\ForNode', false); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/Node/ForLoop.php b/lib/silex/vendor/twig/twig/lib/Twig/Node/ForLoop.php index d330283e7..06477cf1f 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/Node/ForLoop.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/Node/ForLoop.php @@ -3,7 +3,7 @@ /* * This file is part of Twig. * - * (c) 2011 Fabien Potencier + * (c) Fabien Potencier * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -21,11 +21,6 @@ class Twig_Node_ForLoop extends Twig_Node parent::__construct(array(), array('with_loop' => false, 'ifexpr' => false, 'else' => false), $lineno, $tag); } - /** - * Compiles the node to PHP. - * - * @param Twig_Compiler $compiler A Twig_Compiler instance - */ public function compile(Twig_Compiler $compiler) { if ($this->getAttribute('else')) { @@ -53,3 +48,5 @@ class Twig_Node_ForLoop extends Twig_Node } } } + +class_alias('Twig_Node_ForLoop', 'Twig\Node\ForLoopNode', false); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/Node/If.php b/lib/silex/vendor/twig/twig/lib/Twig/Node/If.php index 1b6104dbb..d82edec77 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/Node/If.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/Node/If.php @@ -3,8 +3,8 @@ /* * This file is part of Twig. * - * (c) 2009 Fabien Potencier - * (c) 2009 Armin Ronacher + * (c) Fabien Potencier + * (c) Armin Ronacher * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -19,14 +19,14 @@ class Twig_Node_If extends Twig_Node { public function __construct(Twig_NodeInterface $tests, Twig_NodeInterface $else = null, $lineno, $tag = null) { - parent::__construct(array('tests' => $tests, 'else' => $else), array(), $lineno, $tag); + $nodes = array('tests' => $tests); + if (null !== $else) { + $nodes['else'] = $else; + } + + parent::__construct($nodes, array(), $lineno, $tag); } - /** - * Compiles the node to PHP. - * - * @param Twig_Compiler $compiler A Twig_Compiler instance - */ public function compile(Twig_Compiler $compiler) { $compiler->addDebugInfo($this); @@ -50,7 +50,7 @@ class Twig_Node_If extends Twig_Node ; } - if ($this->hasNode('else') && null !== $this->getNode('else')) { + if ($this->hasNode('else')) { $compiler ->outdent() ->write("} else {\n") @@ -64,3 +64,5 @@ class Twig_Node_If extends Twig_Node ->write("}\n"); } } + +class_alias('Twig_Node_If', 'Twig\Node\IfNode', false); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/Node/Import.php b/lib/silex/vendor/twig/twig/lib/Twig/Node/Import.php index 515ff2aaa..c77e320b7 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/Node/Import.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/Node/Import.php @@ -3,7 +3,7 @@ /* * This file is part of Twig. * - * (c) 2009 Fabien Potencier + * (c) Fabien Potencier * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -21,11 +21,6 @@ class Twig_Node_Import extends Twig_Node parent::__construct(array('expr' => $expr, 'var' => $var), array(), $lineno, $tag); } - /** - * Compiles the node to PHP. - * - * @param Twig_Compiler $compiler A Twig_Compiler instance - */ public function compile(Twig_Compiler $compiler) { $compiler @@ -42,9 +37,9 @@ class Twig_Node_Import extends Twig_Node ->raw('$this->loadTemplate(') ->subcompile($this->getNode('expr')) ->raw(', ') - ->repr($compiler->getFilename()) + ->repr($this->getTemplateName()) ->raw(', ') - ->repr($this->getLine()) + ->repr($this->getTemplateLine()) ->raw(')') ; } @@ -52,3 +47,5 @@ class Twig_Node_Import extends Twig_Node $compiler->raw(";\n"); } } + +class_alias('Twig_Node_Import', 'Twig\Node\ImportNode', false); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/Node/Include.php b/lib/silex/vendor/twig/twig/lib/Twig/Node/Include.php index fecaa8261..2a5114cb8 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/Node/Include.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/Node/Include.php @@ -3,8 +3,8 @@ /* * This file is part of Twig. * - * (c) 2009 Fabien Potencier - * (c) 2009 Armin Ronacher + * (c) Fabien Potencier + * (c) Armin Ronacher * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -19,14 +19,14 @@ class Twig_Node_Include extends Twig_Node implements Twig_NodeOutputInterface { public function __construct(Twig_Node_Expression $expr, Twig_Node_Expression $variables = null, $only = false, $ignoreMissing = false, $lineno, $tag = null) { - parent::__construct(array('expr' => $expr, 'variables' => $variables), array('only' => (bool) $only, 'ignore_missing' => (bool) $ignoreMissing), $lineno, $tag); + $nodes = array('expr' => $expr); + if (null !== $variables) { + $nodes['variables'] = $variables; + } + + parent::__construct($nodes, array('only' => (bool) $only, 'ignore_missing' => (bool) $ignoreMissing), $lineno, $tag); } - /** - * Compiles the node to PHP. - * - * @param Twig_Compiler $compiler A Twig_Compiler instance - */ public function compile(Twig_Compiler $compiler) { $compiler->addDebugInfo($this); @@ -64,16 +64,16 @@ class Twig_Node_Include extends Twig_Node implements Twig_NodeOutputInterface ->write('$this->loadTemplate(') ->subcompile($this->getNode('expr')) ->raw(', ') - ->repr($compiler->getFilename()) + ->repr($this->getTemplateName()) ->raw(', ') - ->repr($this->getLine()) + ->repr($this->getTemplateLine()) ->raw(')') ; } protected function addTemplateArguments(Twig_Compiler $compiler) { - if (null === $this->getNode('variables')) { + if (!$this->hasNode('variables')) { $compiler->raw(false === $this->getAttribute('only') ? '$context' : 'array()'); } elseif (false === $this->getAttribute('only')) { $compiler @@ -86,3 +86,5 @@ class Twig_Node_Include extends Twig_Node implements Twig_NodeOutputInterface } } } + +class_alias('Twig_Node_Include', 'Twig\Node\IncludeNode', false); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/Node/Macro.php b/lib/silex/vendor/twig/twig/lib/Twig/Node/Macro.php index 03cf4dd3d..3cf549770 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/Node/Macro.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/Node/Macro.php @@ -3,7 +3,7 @@ /* * This file is part of Twig. * - * (c) 2009 Fabien Potencier + * (c) Fabien Potencier * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -22,18 +22,13 @@ class Twig_Node_Macro extends Twig_Node { foreach ($arguments as $argumentName => $argument) { if (self::VARARGS_NAME === $argumentName) { - throw new Twig_Error_Syntax(sprintf('The argument "%s" in macro "%s" cannot be defined because the variable "%s" is reserved for arbitrary arguments', self::VARARGS_NAME, $name, self::VARARGS_NAME), $argument->getLine()); + throw new Twig_Error_Syntax(sprintf('The argument "%s" in macro "%s" cannot be defined because the variable "%s" is reserved for arbitrary arguments.', self::VARARGS_NAME, $name, self::VARARGS_NAME), $argument->getTemplateLine()); } } parent::__construct(array('body' => $body, 'arguments' => $arguments), array('name' => $name), $lineno, $tag); } - /** - * Compiles the node to PHP. - * - * @param Twig_Compiler $compiler A Twig_Compiler instance - */ public function compile(Twig_Compiler $compiler) { $compiler @@ -75,7 +70,7 @@ class Twig_Node_Macro extends Twig_Node foreach ($this->getNode('arguments') as $name => $default) { $compiler - ->addIndentation() + ->write('') ->string($name) ->raw(' => $__'.$name.'__') ->raw(",\n") @@ -83,7 +78,7 @@ class Twig_Node_Macro extends Twig_Node } $compiler - ->addIndentation() + ->write('') ->string(self::VARARGS_NAME) ->raw(' => ') ; @@ -114,6 +109,11 @@ class Twig_Node_Macro extends Twig_Node ->write("ob_end_clean();\n\n") ->write("throw \$e;\n") ->outdent() + ->write("} catch (Throwable \$e) {\n") + ->indent() + ->write("ob_end_clean();\n\n") + ->write("throw \$e;\n") + ->outdent() ->write("}\n\n") ->write("return ('' === \$tmp = ob_get_clean()) ? '' : new Twig_Markup(\$tmp, \$this->env->getCharset());\n") ->outdent() @@ -121,3 +121,5 @@ class Twig_Node_Macro extends Twig_Node ; } } + +class_alias('Twig_Node_Macro', 'Twig\Node\MacroNode', false); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/Node/Module.php b/lib/silex/vendor/twig/twig/lib/Twig/Node/Module.php index 1bc4fea6d..5cd8d0502 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/Node/Module.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/Node/Module.php @@ -3,8 +3,8 @@ /* * This file is part of Twig. * - * (c) 2009 Fabien Potencier - * (c) 2009 Armin Ronacher + * (c) Fabien Potencier + * (c) Armin Ronacher * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -21,11 +21,18 @@ */ class Twig_Node_Module extends Twig_Node { - public function __construct(Twig_NodeInterface $body, Twig_Node_Expression $parent = null, Twig_NodeInterface $blocks, Twig_NodeInterface $macros, Twig_NodeInterface $traits, $embeddedTemplates, $filename) + private $source; + + public function __construct(Twig_NodeInterface $body, Twig_Node_Expression $parent = null, Twig_NodeInterface $blocks, Twig_NodeInterface $macros, Twig_NodeInterface $traits, $embeddedTemplates, $name, $source = '') { - // embedded templates are set as attributes so that they are only visited once by the visitors - parent::__construct(array( - 'parent' => $parent, + if (!$name instanceof Twig_Source) { + @trigger_error(sprintf('Passing a string as the $name argument of %s() is deprecated since version 1.27. Pass a Twig_Source instance instead.', __METHOD__), E_USER_DEPRECATED); + $this->source = new Twig_Source($source, $name); + } else { + $this->source = $name; + } + + $nodes = array( 'body' => $body, 'blocks' => $blocks, 'macros' => $macros, @@ -35,11 +42,23 @@ class Twig_Node_Module extends Twig_Node 'constructor_start' => new Twig_Node(), 'constructor_end' => new Twig_Node(), 'class_end' => new Twig_Node(), - ), array( - 'filename' => $filename, + ); + if (null !== $parent) { + $nodes['parent'] = $parent; + } + + // embedded templates are set as attributes so that they are only visited once by the visitors + parent::__construct($nodes, array( + // source to be remove in 2.0 + 'source' => $this->source->getCode(), + // filename to be remove in 2.0 (use getTemplateName() instead) + 'filename' => $this->source->getName(), 'index' => null, 'embedded_templates' => $embeddedTemplates, ), 1); + + // populate the template name of all node children + $this->setTemplateName($this->source->getName()); } public function setIndex($index) @@ -47,11 +66,6 @@ class Twig_Node_Module extends Twig_Node $this->setAttribute('index', $index); } - /** - * Compiles the node to PHP. - * - * @param Twig_Compiler $compiler A Twig_Compiler instance - */ public function compile(Twig_Compiler $compiler) { $this->compileTemplate($compiler); @@ -72,7 +86,7 @@ class Twig_Node_Module extends Twig_Node if ( count($this->getNode('blocks')) || count($this->getNode('traits')) - || null === $this->getNode('parent') + || !$this->hasNode('parent') || $this->getNode('parent') instanceof Twig_Node_Expression_Constant || count($this->getNode('constructor_start')) || count($this->getNode('constructor_end')) @@ -94,14 +108,19 @@ class Twig_Node_Module extends Twig_Node $this->compileDebugInfo($compiler); + $this->compileGetSource($compiler); + + $this->compileGetSourceContext($compiler); + $this->compileClassFooter($compiler); } protected function compileGetParent(Twig_Compiler $compiler) { - if (null === $parent = $this->getNode('parent')) { + if (!$this->hasNode('parent')) { return; } + $parent = $this->getNode('parent'); $compiler ->write("protected function doGetParent(array \$context)\n", "{\n") @@ -117,9 +136,9 @@ class Twig_Node_Module extends Twig_Node ->raw('$this->loadTemplate(') ->subcompile($parent) ->raw(', ') - ->repr($compiler->getFilename()) + ->repr($this->source->getName()) ->raw(', ') - ->repr($this->getNode('parent')->getLine()) + ->repr($parent->getTemplateLine()) ->raw(')') ; } @@ -135,9 +154,9 @@ class Twig_Node_Module extends Twig_Node { $compiler ->write("\n\n") - // if the filename contains */, add a blank to avoid a PHP parse error - ->write('/* '.str_replace('*/', '* /', $this->getAttribute('filename'))." */\n") - ->write('class '.$compiler->getEnvironment()->getTemplateClass($this->getAttribute('filename'), $this->getAttribute('index'))) + // if the template name contains */, add a blank to avoid a PHP parse error + ->write('/* '.str_replace('*/', '* /', $this->source->getName())." */\n") + ->write('class '.$compiler->getEnvironment()->getTemplateClass($this->source->getName(), $this->getAttribute('index'))) ->raw(sprintf(" extends %s\n", $compiler->getEnvironment()->getBaseTemplateClass())) ->write("{\n") ->indent() @@ -154,17 +173,17 @@ class Twig_Node_Module extends Twig_Node ; // parent - if (null === $parent = $this->getNode('parent')) { + if (!$this->hasNode('parent')) { $compiler->write("\$this->parent = false;\n\n"); - } elseif ($parent instanceof Twig_Node_Expression_Constant) { + } elseif (($parent = $this->getNode('parent')) && $parent instanceof Twig_Node_Expression_Constant) { $compiler ->addDebugInfo($parent) ->write('$this->parent = $this->loadTemplate(') ->subcompile($parent) ->raw(', ') - ->repr($compiler->getFilename()) + ->repr($this->source->getName()) ->raw(', ') - ->repr($this->getNode('parent')->getLine()) + ->repr($parent->getTemplateLine()) ->raw(");\n") ; } @@ -282,7 +301,8 @@ class Twig_Node_Module extends Twig_Node ->subcompile($this->getNode('body')) ; - if (null !== $parent = $this->getNode('parent')) { + if ($this->hasNode('parent')) { + $parent = $this->getNode('parent'); $compiler->addDebugInfo($parent); if ($parent instanceof Twig_Node_Expression_Constant) { $compiler->write('$this->parent'); @@ -319,7 +339,7 @@ class Twig_Node_Module extends Twig_Node ->write("public function getTemplateName()\n", "{\n") ->indent() ->write('return ') - ->repr($this->getAttribute('filename')) + ->repr($this->source->getName()) ->raw(";\n") ->outdent() ->write("}\n\n") @@ -335,7 +355,7 @@ class Twig_Node_Module extends Twig_Node // // Put another way, a template can be used as a trait if it // only contains blocks and use statements. - $traitable = null === $this->getNode('parent') && 0 === count($this->getNode('macros')); + $traitable = !$this->hasNode('parent') && 0 === count($this->getNode('macros')); if ($traitable) { if ($this->getNode('body') instanceof Twig_Node_Body) { $nodes = $this->getNode('body')->getNode(0); @@ -385,6 +405,37 @@ class Twig_Node_Module extends Twig_Node ->indent() ->write(sprintf("return %s;\n", str_replace("\n", '', var_export(array_reverse($compiler->getDebugInfo(), true), true)))) ->outdent() + ->write("}\n\n") + ; + } + + protected function compileGetSource(Twig_Compiler $compiler) + { + $compiler + ->write("/** @deprecated since 1.27 (to be removed in 2.0). Use getSourceContext() instead */\n") + ->write("public function getSource()\n", "{\n") + ->indent() + ->write("@trigger_error('The '.__METHOD__.' method is deprecated since version 1.27 and will be removed in 2.0. Use getSourceContext() instead.', E_USER_DEPRECATED);\n\n") + ->write('return $this->getSourceContext()->getCode();') + ->raw("\n") + ->outdent() + ->write("}\n\n") + ; + } + + protected function compileGetSourceContext(Twig_Compiler $compiler) + { + $compiler + ->write("public function getSourceContext()\n", "{\n") + ->indent() + ->write('return new Twig_Source(') + ->string($compiler->getEnvironment()->isDebug() ? $this->source->getCode() : '') + ->raw(', ') + ->string($this->source->getName()) + ->raw(', ') + ->string($this->source->getPath()) + ->raw(");\n") + ->outdent() ->write("}\n") ; } @@ -396,13 +447,15 @@ class Twig_Node_Module extends Twig_Node ->write(sprintf('%s = $this->loadTemplate(', $var)) ->subcompile($node) ->raw(', ') - ->repr($compiler->getFilename()) + ->repr($node->getTemplateName()) ->raw(', ') - ->repr($node->getLine()) + ->repr($node->getTemplateLine()) ->raw(");\n") ; } else { - throw new LogicException('Trait templates can only be constant nodes'); + throw new LogicException('Trait templates can only be constant nodes.'); } } } + +class_alias('Twig_Node_Module', 'Twig\Node\ModuleNode', false); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/Node/Print.php b/lib/silex/vendor/twig/twig/lib/Twig/Node/Print.php index 42635361a..374db89b7 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/Node/Print.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/Node/Print.php @@ -3,8 +3,8 @@ /* * This file is part of Twig. * - * (c) 2009 Fabien Potencier - * (c) 2009 Armin Ronacher + * (c) Fabien Potencier + * (c) Armin Ronacher * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -22,11 +22,6 @@ class Twig_Node_Print extends Twig_Node implements Twig_NodeOutputInterface parent::__construct(array('expr' => $expr), array(), $lineno, $tag); } - /** - * Compiles the node to PHP. - * - * @param Twig_Compiler $compiler A Twig_Compiler instance - */ public function compile(Twig_Compiler $compiler) { $compiler @@ -37,3 +32,5 @@ class Twig_Node_Print extends Twig_Node implements Twig_NodeOutputInterface ; } } + +class_alias('Twig_Node_Print', 'Twig\Node\PrintNode', false); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/Node/Sandbox.php b/lib/silex/vendor/twig/twig/lib/Twig/Node/Sandbox.php index 8ca772bcd..44b30ab9c 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/Node/Sandbox.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/Node/Sandbox.php @@ -3,7 +3,7 @@ /* * This file is part of Twig. * - * (c) 2010 Fabien Potencier + * (c) Fabien Potencier * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -21,16 +21,11 @@ class Twig_Node_Sandbox extends Twig_Node parent::__construct(array('body' => $body), array(), $lineno, $tag); } - /** - * Compiles the node to PHP. - * - * @param Twig_Compiler $compiler A Twig_Compiler instance - */ public function compile(Twig_Compiler $compiler) { $compiler ->addDebugInfo($this) - ->write("\$sandbox = \$this->env->getExtension('sandbox');\n") + ->write("\$sandbox = \$this->env->getExtension('Twig_Extension_Sandbox');\n") ->write("if (!\$alreadySandboxed = \$sandbox->isSandboxed()) {\n") ->indent() ->write("\$sandbox->enableSandbox();\n") @@ -45,3 +40,5 @@ class Twig_Node_Sandbox extends Twig_Node ; } } + +class_alias('Twig_Node_Sandbox', 'Twig\Node\SandboxNode', false); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/Node/SandboxedPrint.php b/lib/silex/vendor/twig/twig/lib/Twig/Node/SandboxedPrint.php index 823e7acc7..a08f21f56 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/Node/SandboxedPrint.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/Node/SandboxedPrint.php @@ -3,7 +3,7 @@ /* * This file is part of Twig. * - * (c) 2010 Fabien Potencier + * (c) Fabien Potencier * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -21,21 +21,11 @@ */ class Twig_Node_SandboxedPrint extends Twig_Node_Print { - public function __construct(Twig_Node_Expression $expr, $lineno, $tag = null) - { - parent::__construct($expr, $lineno, $tag); - } - - /** - * Compiles the node to PHP. - * - * @param Twig_Compiler $compiler A Twig_Compiler instance - */ public function compile(Twig_Compiler $compiler) { $compiler ->addDebugInfo($this) - ->write('echo $this->env->getExtension(\'sandbox\')->ensureToStringAllowed(') + ->write('echo $this->env->getExtension(\'Twig_Extension_Sandbox\')->ensureToStringAllowed(') ->subcompile($this->getNode('expr')) ->raw(");\n") ; @@ -46,11 +36,9 @@ class Twig_Node_SandboxedPrint extends Twig_Node_Print * * This is mostly needed when another visitor adds filters (like the escaper one). * - * @param Twig_Node $node A Node - * * @return Twig_Node */ - protected function removeNodeFilter($node) + protected function removeNodeFilter(Twig_Node $node) { if ($node instanceof Twig_Node_Expression_Filter) { return $this->removeNodeFilter($node->getNode('node')); @@ -59,3 +47,5 @@ class Twig_Node_SandboxedPrint extends Twig_Node_Print return $node; } } + +class_alias('Twig_Node_SandboxedPrint', 'Twig\Node\SandboxedPrintNode', false); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/Node/Set.php b/lib/silex/vendor/twig/twig/lib/Twig/Node/Set.php index 407d14734..6c6743eeb 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/Node/Set.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/Node/Set.php @@ -3,7 +3,7 @@ /* * This file is part of Twig. * - * (c) 2010 Fabien Potencier + * (c) Fabien Potencier * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,7 @@ * * @author Fabien Potencier */ -class Twig_Node_Set extends Twig_Node +class Twig_Node_Set extends Twig_Node implements Twig_NodeCaptureInterface { public function __construct($capture, Twig_NodeInterface $names, Twig_NodeInterface $values, $lineno, $tag = null) { @@ -30,17 +30,12 @@ class Twig_Node_Set extends Twig_Node $values = $this->getNode('values'); if ($values instanceof Twig_Node_Text) { - $this->setNode('values', new Twig_Node_Expression_Constant($values->getAttribute('data'), $values->getLine())); + $this->setNode('values', new Twig_Node_Expression_Constant($values->getAttribute('data'), $values->getTemplateLine())); $this->setAttribute('capture', false); } } } - /** - * Compiles the node to PHP. - * - * @param Twig_Compiler $compiler A Twig_Compiler instance - */ public function compile(Twig_Compiler $compiler) { $compiler->addDebugInfo($this); @@ -99,3 +94,5 @@ class Twig_Node_Set extends Twig_Node $compiler->raw(";\n"); } } + +class_alias('Twig_Node_Set', 'Twig\Node\SetNode', false); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/Node/SetTemp.php b/lib/silex/vendor/twig/twig/lib/Twig/Node/SetTemp.php index 3bdd1cb74..996fdcde7 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/Node/SetTemp.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/Node/SetTemp.php @@ -3,12 +3,15 @@ /* * This file is part of Twig. * - * (c) 2011 Fabien Potencier + * (c) Fabien Potencier * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ +/** + * @internal + */ class Twig_Node_SetTemp extends Twig_Node { public function __construct($name, $lineno) @@ -33,3 +36,5 @@ class Twig_Node_SetTemp extends Twig_Node ; } } + +class_alias('Twig_Node_SetTemp', 'Twig\Node\SetTempNode', false); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/Node/Spaceless.php b/lib/silex/vendor/twig/twig/lib/Twig/Node/Spaceless.php index 1478c59ac..76f90cde5 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/Node/Spaceless.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/Node/Spaceless.php @@ -3,7 +3,7 @@ /* * This file is part of Twig. * - * (c) 2010 Fabien Potencier + * (c) Fabien Potencier * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -23,11 +23,6 @@ class Twig_Node_Spaceless extends Twig_Node parent::__construct(array('body' => $body), array(), $lineno, $tag); } - /** - * Compiles the node to PHP. - * - * @param Twig_Compiler $compiler A Twig_Compiler instance - */ public function compile(Twig_Compiler $compiler) { $compiler @@ -38,3 +33,5 @@ class Twig_Node_Spaceless extends Twig_Node ; } } + +class_alias('Twig_Node_Spaceless', 'Twig\Node\SpacelessNode', false); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/Node/Text.php b/lib/silex/vendor/twig/twig/lib/Twig/Node/Text.php index 6863604e5..f4577fee2 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/Node/Text.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/Node/Text.php @@ -3,8 +3,8 @@ /* * This file is part of Twig. * - * (c) 2009 Fabien Potencier - * (c) 2009 Armin Ronacher + * (c) Fabien Potencier + * (c) Armin Ronacher * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -22,11 +22,6 @@ class Twig_Node_Text extends Twig_Node implements Twig_NodeOutputInterface parent::__construct(array(), array('data' => $data), $lineno); } - /** - * Compiles the node to PHP. - * - * @param Twig_Compiler $compiler A Twig_Compiler instance - */ public function compile(Twig_Compiler $compiler) { $compiler @@ -37,3 +32,5 @@ class Twig_Node_Text extends Twig_Node implements Twig_NodeOutputInterface ; } } + +class_alias('Twig_Node_Text', 'Twig\Node\TextNode', false); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/Node/With.php b/lib/silex/vendor/twig/twig/lib/Twig/Node/With.php new file mode 100644 index 000000000..2ab0ea5d7 --- /dev/null +++ b/lib/silex/vendor/twig/twig/lib/Twig/Node/With.php @@ -0,0 +1,64 @@ + + */ +class Twig_Node_With extends Twig_Node +{ + public function __construct(Twig_Node $body, Twig_Node $variables = null, $only = false, $lineno, $tag = null) + { + $nodes = array('body' => $body); + if (null !== $variables) { + $nodes['variables'] = $variables; + } + + parent::__construct($nodes, array('only' => (bool) $only), $lineno, $tag); + } + + public function compile(Twig_Compiler $compiler) + { + $compiler->addDebugInfo($this); + + if ($this->hasNode('variables')) { + $varsName = $compiler->getVarName(); + $compiler + ->write(sprintf('$%s = ', $varsName)) + ->subcompile($this->getNode('variables')) + ->raw(";\n") + ->write(sprintf("if (!is_array(\$%s)) {\n", $varsName)) + ->indent() + ->write("throw new Twig_Error_Runtime('Variables passed to the \"with\" tag must be a hash.');\n") + ->outdent() + ->write("}\n") + ; + + if ($this->getAttribute('only')) { + $compiler->write("\$context = array('_parent' => \$context);\n"); + } else { + $compiler->write("\$context['_parent'] = \$context;\n"); + } + + $compiler->write(sprintf("\$context = array_merge(\$context, \$%s);\n", $varsName)); + } else { + $compiler->write("\$context['_parent'] = \$context;\n"); + } + + $compiler + ->subcompile($this->getNode('body')) + ->write("\$context = \$context['_parent'];\n") + ; + } +} + +class_alias('Twig_Node_With', 'Twig\Node\WithNode', false); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/NodeCaptureInterface.php b/lib/silex/vendor/twig/twig/lib/Twig/NodeCaptureInterface.php new file mode 100644 index 000000000..6638834b1 --- /dev/null +++ b/lib/silex/vendor/twig/twig/lib/Twig/NodeCaptureInterface.php @@ -0,0 +1,21 @@ + + */ +interface Twig_NodeCaptureInterface +{ +} + +class_alias('Twig_NodeCaptureInterface', 'Twig\Node\NodeCaptureInterface', false); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/NodeInterface.php b/lib/silex/vendor/twig/twig/lib/Twig/NodeInterface.php index 8077349b3..78e758bd4 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/NodeInterface.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/NodeInterface.php @@ -3,7 +3,7 @@ /* * This file is part of Twig. * - * (c) 2010 Fabien Potencier + * (c) Fabien Potencier * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -20,11 +20,12 @@ interface Twig_NodeInterface extends Countable, IteratorAggregate { /** * Compiles the node to PHP. - * - * @param Twig_Compiler $compiler A Twig_Compiler instance */ public function compile(Twig_Compiler $compiler); + /** + * @deprecated since 1.27 (to be removed in 2.0) + */ public function getLine(); public function getNodeTag(); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/NodeOutputInterface.php b/lib/silex/vendor/twig/twig/lib/Twig/NodeOutputInterface.php index 22172c09d..5a8eaa9c3 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/NodeOutputInterface.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/NodeOutputInterface.php @@ -3,7 +3,7 @@ /* * This file is part of Twig. * - * (c) 2010 Fabien Potencier + * (c) Fabien Potencier * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -17,3 +17,5 @@ interface Twig_NodeOutputInterface { } + +class_alias('Twig_NodeOutputInterface', 'Twig\Node\NodeOutputInterface', false); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/NodeTraverser.php b/lib/silex/vendor/twig/twig/lib/Twig/NodeTraverser.php index 00f7b54b6..787629ce3 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/NodeTraverser.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/NodeTraverser.php @@ -3,7 +3,7 @@ /* * This file is part of Twig. * - * (c) 2009 Fabien Potencier + * (c) Fabien Potencier * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,6 +14,8 @@ * * It visits all nodes and their children and calls the given visitor for each. * + * @final + * * @author Fabien Potencier */ class Twig_NodeTraverser @@ -22,10 +24,8 @@ class Twig_NodeTraverser protected $visitors = array(); /** - * Constructor. - * - * @param Twig_Environment $env A Twig_Environment instance - * @param Twig_NodeVisitorInterface[] $visitors An array of Twig_NodeVisitorInterface instances + * @param Twig_Environment $env + * @param Twig_NodeVisitorInterface[] $visitors */ public function __construct(Twig_Environment $env, array $visitors = array()) { @@ -35,11 +35,6 @@ class Twig_NodeTraverser } } - /** - * Adds a visitor. - * - * @param Twig_NodeVisitorInterface $visitor A Twig_NodeVisitorInterface instance - */ public function addVisitor(Twig_NodeVisitorInterface $visitor) { if (!isset($this->visitors[$visitor->getPriority()])) { @@ -52,8 +47,6 @@ class Twig_NodeTraverser /** * Traverses a node and calls the registered visitors. * - * @param Twig_NodeInterface $node A Twig_NodeInterface instance - * * @return Twig_NodeInterface */ public function traverse(Twig_NodeInterface $node) @@ -87,3 +80,5 @@ class Twig_NodeTraverser return $visitor->leaveNode($node, $this->env); } } + +class_alias('Twig_NodeTraverser', 'Twig\NodeTraverser', false); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/NodeVisitor/Escaper.php b/lib/silex/vendor/twig/twig/lib/Twig/NodeVisitor/Escaper.php index 5c9497774..1a1ae66f7 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/NodeVisitor/Escaper.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/NodeVisitor/Escaper.php @@ -3,7 +3,7 @@ /* * This file is part of Twig. * - * (c) 2009 Fabien Potencier + * (c) Fabien Potencier * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -12,6 +12,8 @@ /** * Twig_NodeVisitor_Escaper implements output escaping. * + * @final + * * @author Fabien Potencier */ class Twig_NodeVisitor_Escaper extends Twig_BaseNodeVisitor @@ -28,16 +30,14 @@ class Twig_NodeVisitor_Escaper extends Twig_BaseNodeVisitor $this->safeAnalysis = new Twig_NodeVisitor_SafeAnalysis(); } - /** - * {@inheritdoc} - */ protected function doEnterNode(Twig_Node $node, Twig_Environment $env) { if ($node instanceof Twig_Node_Module) { - if ($env->hasExtension('escaper') && $defaultStrategy = $env->getExtension('escaper')->getDefaultStrategy($node->getAttribute('filename'))) { + if ($env->hasExtension('Twig_Extension_Escaper') && $defaultStrategy = $env->getExtension('Twig_Extension_Escaper')->getDefaultStrategy($node->getTemplateName())) { $this->defaultStrategy = $defaultStrategy; } $this->safeVars = array(); + $this->blocks = array(); } elseif ($node instanceof Twig_Node_AutoEscape) { $this->statusStack[] = $node->getAttribute('value'); } elseif ($node instanceof Twig_Node_Block) { @@ -49,14 +49,12 @@ class Twig_NodeVisitor_Escaper extends Twig_BaseNodeVisitor return $node; } - /** - * {@inheritdoc} - */ protected function doLeaveNode(Twig_Node $node, Twig_Environment $env) { if ($node instanceof Twig_Node_Module) { $this->defaultStrategy = false; $this->safeVars = array(); + $this->blocks = array(); } elseif ($node instanceof Twig_Node_Expression_Filter) { return $this->preEscapeFilterNode($node, $env); } elseif ($node instanceof Twig_Node_Print) { @@ -88,7 +86,7 @@ class Twig_NodeVisitor_Escaper extends Twig_BaseNodeVisitor return new $class( $this->getEscaperFilter($type, $expression), - $node->getLine() + $node->getTemplateLine() ); } @@ -140,18 +138,17 @@ class Twig_NodeVisitor_Escaper extends Twig_BaseNodeVisitor protected function getEscaperFilter($type, Twig_NodeInterface $node) { - $line = $node->getLine(); + $line = $node->getTemplateLine(); $name = new Twig_Node_Expression_Constant('escape', $line); $args = new Twig_Node(array(new Twig_Node_Expression_Constant((string) $type, $line), new Twig_Node_Expression_Constant(null, $line), new Twig_Node_Expression_Constant(true, $line))); return new Twig_Node_Expression_Filter($node, $name, $args, $line); } - /** - * {@inheritdoc} - */ public function getPriority() { return 0; } } + +class_alias('Twig_NodeVisitor_Escaper', 'Twig\NodeVisitor\EscaperNodeVisitor', false); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/NodeVisitor/Optimizer.php b/lib/silex/vendor/twig/twig/lib/Twig/NodeVisitor/Optimizer.php index 872b7feaa..2fa19f3d7 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/NodeVisitor/Optimizer.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/NodeVisitor/Optimizer.php @@ -3,7 +3,7 @@ /* * This file is part of Twig. * - * (c) 2010 Fabien Potencier + * (c) Fabien Potencier * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -17,6 +17,8 @@ * You can configure which optimizations you want to activate via the * optimizer mode. * + * @final + * * @author Fabien Potencier */ class Twig_NodeVisitor_Optimizer extends Twig_BaseNodeVisitor @@ -34,8 +36,6 @@ class Twig_NodeVisitor_Optimizer extends Twig_BaseNodeVisitor protected $inABody = false; /** - * Constructor. - * * @param int $optimizers The optimizer mode */ public function __construct($optimizers = -1) @@ -47,16 +47,13 @@ class Twig_NodeVisitor_Optimizer extends Twig_BaseNodeVisitor $this->optimizers = $optimizers; } - /** - * {@inheritdoc} - */ protected function doEnterNode(Twig_Node $node, Twig_Environment $env) { if (self::OPTIMIZE_FOR === (self::OPTIMIZE_FOR & $this->optimizers)) { $this->enterOptimizeFor($node, $env); } - if (PHP_VERSION_ID < 50400 && self::OPTIMIZE_VAR_ACCESS === (self::OPTIMIZE_VAR_ACCESS & $this->optimizers) && !$env->isStrictVariables() && !$env->hasExtension('sandbox')) { + if (PHP_VERSION_ID < 50400 && self::OPTIMIZE_VAR_ACCESS === (self::OPTIMIZE_VAR_ACCESS & $this->optimizers) && !$env->isStrictVariables() && !$env->hasExtension('Twig_Extension_Sandbox')) { if ($this->inABody) { if (!$node instanceof Twig_Node_Expression) { if (get_class($node) !== 'Twig_Node') { @@ -73,9 +70,6 @@ class Twig_NodeVisitor_Optimizer extends Twig_BaseNodeVisitor return $node; } - /** - * {@inheritdoc} - */ protected function doLeaveNode(Twig_Node $node, Twig_Environment $env) { $expression = $node instanceof Twig_Node_Expression; @@ -90,14 +84,14 @@ class Twig_NodeVisitor_Optimizer extends Twig_BaseNodeVisitor $node = $this->optimizePrintNode($node, $env); - if (self::OPTIMIZE_VAR_ACCESS === (self::OPTIMIZE_VAR_ACCESS & $this->optimizers) && !$env->isStrictVariables() && !$env->hasExtension('sandbox')) { + if (self::OPTIMIZE_VAR_ACCESS === (self::OPTIMIZE_VAR_ACCESS & $this->optimizers) && !$env->isStrictVariables() && !$env->hasExtension('Twig_Extension_Sandbox')) { if ($node instanceof Twig_Node_Body) { $this->inABody = false; } elseif ($this->inABody) { if (!$expression && get_class($node) !== 'Twig_Node' && $prependedNodes = array_shift($this->prependedNodes)) { $nodes = array(); foreach (array_unique($prependedNodes) as $name) { - $nodes[] = new Twig_Node_SetTemp($name, $node->getLine()); + $nodes[] = new Twig_Node_SetTemp($name, $node->getTemplateLine()); } $nodes[] = $node; @@ -114,7 +108,7 @@ class Twig_NodeVisitor_Optimizer extends Twig_BaseNodeVisitor if ('Twig_Node_Expression_Name' === get_class($node) && $node->isSimple()) { $this->prependedNodes[0][] = $node->getAttribute('name'); - return new Twig_Node_Expression_TempName($node->getAttribute('name'), $node->getLine()); + return new Twig_Node_Expression_TempName($node->getAttribute('name'), $node->getTemplateLine()); } return $node; @@ -127,9 +121,6 @@ class Twig_NodeVisitor_Optimizer extends Twig_BaseNodeVisitor * * * "echo $this->render(Parent)Block()" with "$this->display(Parent)Block()" * - * @param Twig_NodeInterface $node A Node - * @param Twig_Environment $env The current Twig environment - * * @return Twig_NodeInterface */ protected function optimizePrintNode(Twig_NodeInterface $node, Twig_Environment $env) @@ -138,13 +129,14 @@ class Twig_NodeVisitor_Optimizer extends Twig_BaseNodeVisitor return $node; } + $exprNode = $node->getNode('expr'); if ( - $node->getNode('expr') instanceof Twig_Node_Expression_BlockReference || - $node->getNode('expr') instanceof Twig_Node_Expression_Parent + $exprNode instanceof Twig_Node_Expression_BlockReference || + $exprNode instanceof Twig_Node_Expression_Parent ) { - $node->getNode('expr')->setAttribute('output', true); + $exprNode->setAttribute('output', true); - return $node->getNode('expr'); + return $exprNode; } return $node; @@ -153,9 +145,6 @@ class Twig_NodeVisitor_Optimizer extends Twig_BaseNodeVisitor /** * Removes "raw" filters. * - * @param Twig_NodeInterface $node A Node - * @param Twig_Environment $env The current Twig environment - * * @return Twig_NodeInterface */ protected function optimizeRawFilter(Twig_NodeInterface $node, Twig_Environment $env) @@ -169,9 +158,6 @@ class Twig_NodeVisitor_Optimizer extends Twig_BaseNodeVisitor /** * Optimizes "for" tag by removing the "loop" variable creation whenever possible. - * - * @param Twig_NodeInterface $node A Node - * @param Twig_Environment $env The current Twig environment */ protected function enterOptimizeFor(Twig_NodeInterface $node, Twig_Environment $env) { @@ -236,9 +222,6 @@ class Twig_NodeVisitor_Optimizer extends Twig_BaseNodeVisitor /** * Optimizes "for" tag by removing the "loop" variable creation whenever possible. - * - * @param Twig_NodeInterface $node A Node - * @param Twig_Environment $env The current Twig environment */ protected function leaveOptimizeFor(Twig_NodeInterface $node, Twig_Environment $env) { @@ -261,11 +244,10 @@ class Twig_NodeVisitor_Optimizer extends Twig_BaseNodeVisitor } } - /** - * {@inheritdoc} - */ public function getPriority() { return 255; } } + +class_alias('Twig_NodeVisitor_Optimizer', 'Twig\NodeVisitor\OptimizerNodeVisitor', false); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/NodeVisitor/SafeAnalysis.php b/lib/silex/vendor/twig/twig/lib/Twig/NodeVisitor/SafeAnalysis.php index 439f5bf49..ca31c8fc7 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/NodeVisitor/SafeAnalysis.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/NodeVisitor/SafeAnalysis.php @@ -9,6 +9,9 @@ * file that was distributed with this source code. */ +/** + * @final + */ class Twig_NodeVisitor_SafeAnalysis extends Twig_BaseNodeVisitor { protected $data = array(); @@ -57,17 +60,11 @@ class Twig_NodeVisitor_SafeAnalysis extends Twig_BaseNodeVisitor ); } - /** - * {@inheritdoc} - */ protected function doEnterNode(Twig_Node $node, Twig_Environment $env) { return $node; } - /** - * {@inheritdoc} - */ protected function doLeaveNode(Twig_Node $node, Twig_Environment $env) { if ($node instanceof Twig_Node_Expression_Constant) { @@ -144,11 +141,10 @@ class Twig_NodeVisitor_SafeAnalysis extends Twig_BaseNodeVisitor return array_intersect($a, $b); } - /** - * {@inheritdoc} - */ public function getPriority() { return 0; } } + +class_alias('Twig_NodeVisitor_SafeAnalysis', 'Twig\NodeVisitor\SafeAnalysisNodeVisitor', false); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/NodeVisitor/Sandbox.php b/lib/silex/vendor/twig/twig/lib/Twig/NodeVisitor/Sandbox.php index 7f1b913b7..b631b29d4 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/NodeVisitor/Sandbox.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/NodeVisitor/Sandbox.php @@ -3,7 +3,7 @@ /* * This file is part of Twig. * - * (c) 2009 Fabien Potencier + * (c) Fabien Potencier * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -12,6 +12,8 @@ /** * Twig_NodeVisitor_Sandbox implements sandboxing. * + * @final + * * @author Fabien Potencier */ class Twig_NodeVisitor_Sandbox extends Twig_BaseNodeVisitor @@ -21,9 +23,6 @@ class Twig_NodeVisitor_Sandbox extends Twig_BaseNodeVisitor protected $filters; protected $functions; - /** - * {@inheritdoc} - */ protected function doEnterNode(Twig_Node $node, Twig_Environment $env) { if ($node instanceof Twig_Node_Module) { @@ -51,16 +50,13 @@ class Twig_NodeVisitor_Sandbox extends Twig_BaseNodeVisitor // wrap print to check __toString() calls if ($node instanceof Twig_Node_Print) { - return new Twig_Node_SandboxedPrint($node->getNode('expr'), $node->getLine(), $node->getNodeTag()); + return new Twig_Node_SandboxedPrint($node->getNode('expr'), $node->getTemplateLine(), $node->getNodeTag()); } } return $node; } - /** - * {@inheritdoc} - */ protected function doLeaveNode(Twig_Node $node, Twig_Environment $env) { if ($node instanceof Twig_Node_Module) { @@ -72,11 +68,10 @@ class Twig_NodeVisitor_Sandbox extends Twig_BaseNodeVisitor return $node; } - /** - * {@inheritdoc} - */ public function getPriority() { return 0; } } + +class_alias('Twig_NodeVisitor_Sandbox', 'Twig\NodeVisitor\SandboxNodeVisitor', false); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/NodeVisitorInterface.php b/lib/silex/vendor/twig/twig/lib/Twig/NodeVisitorInterface.php index f27616302..1270a3724 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/NodeVisitorInterface.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/NodeVisitorInterface.php @@ -3,7 +3,7 @@ /* * This file is part of Twig. * - * (c) 2009 Fabien Potencier + * (c) Fabien Potencier * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -19,9 +19,6 @@ interface Twig_NodeVisitorInterface /** * Called before child nodes are visited. * - * @param Twig_NodeInterface $node The node to visit - * @param Twig_Environment $env The Twig environment instance - * * @return Twig_NodeInterface The modified node */ public function enterNode(Twig_NodeInterface $node, Twig_Environment $env); @@ -29,9 +26,6 @@ interface Twig_NodeVisitorInterface /** * Called after child nodes are visited. * - * @param Twig_NodeInterface $node The node to visit - * @param Twig_Environment $env The Twig environment instance - * * @return Twig_NodeInterface|false The modified node or false if the node must be removed */ public function leaveNode(Twig_NodeInterface $node, Twig_Environment $env); @@ -45,3 +39,7 @@ interface Twig_NodeVisitorInterface */ public function getPriority(); } + +class_alias('Twig_NodeVisitorInterface', 'Twig\NodeVisitor\NodeVisitorInterface', false); +class_exists('Twig_Environment'); +class_exists('Twig_Node'); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/Parser.php b/lib/silex/vendor/twig/twig/lib/Twig/Parser.php index 6f24ee659..e796f197a 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/Parser.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/Parser.php @@ -3,8 +3,8 @@ /* * This file is part of Twig. * - * (c) 2009 Fabien Potencier - * (c) 2009 Armin Ronacher + * (c) Fabien Potencier + * (c) Armin Ronacher * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -32,18 +32,18 @@ class Twig_Parser implements Twig_ParserInterface protected $traits; protected $embeddedTemplates = array(); - /** - * Constructor. - * - * @param Twig_Environment $env A Twig_Environment instance - */ public function __construct(Twig_Environment $env) { $this->env = $env; } + /** + * @deprecated since 1.27 (to be removed in 2.0) + */ public function getEnvironment() { + @trigger_error('The '.__METHOD__.' method is deprecated since version 1.27 and will be removed in 2.0.', E_USER_DEPRECATED); + return $this->env; } @@ -52,18 +52,26 @@ class Twig_Parser implements Twig_ParserInterface return sprintf('__internal_%s', hash('sha256', uniqid(mt_rand(), true), false)); } + /** + * @deprecated since 1.27 (to be removed in 2.0). Use $parser->getStream()->getSourceContext()->getPath() instead. + */ public function getFilename() { - return $this->stream->getFilename(); + @trigger_error(sprintf('The "%s" method is deprecated since version 1.27 and will be removed in 2.0. Use $parser->getStream()->getSourceContext()->getPath() instead.', __METHOD__), E_USER_DEPRECATED); + + return $this->stream->getSourceContext()->getName(); } - /** - * {@inheritdoc} - */ public function parse(Twig_TokenStream $stream, $test = null, $dropNeedle = false) { // push all variables into the stack to keep the current state of the parser - $vars = get_object_vars($this); + // using get_object_vars() instead of foreach would lead to https://bugs.php.net/71336 + // This hack can be removed when min version if PHP 7.0 + $vars = array(); + foreach ($this as $k => $v) { + $vars[$k] = $v; + } + unset($vars['stack'], $vars['env'], $vars['handlers'], $vars['visitors'], $vars['expressionParser'], $vars['reservedMacroNames']); $this->stack[] = $vars; @@ -79,7 +87,7 @@ class Twig_Parser implements Twig_ParserInterface } if (null === $this->expressionParser) { - $this->expressionParser = new Twig_ExpressionParser($this, $this->env->getUnaryOperators(), $this->env->getBinaryOperators()); + $this->expressionParser = new Twig_ExpressionParser($this, $this->env); } $this->stream = $stream; @@ -94,14 +102,12 @@ class Twig_Parser implements Twig_ParserInterface try { $body = $this->subparse($test, $dropNeedle); - if (null !== $this->parent) { - if (null === $body = $this->filterBodyNodes($body)) { - $body = new Twig_Node(); - } + if (null !== $this->parent && null === $body = $this->filterBodyNodes($body)) { + $body = new Twig_Node(); } } catch (Twig_Error_Syntax $e) { - if (!$e->getTemplateFile()) { - $e->setTemplateFile($this->getFilename()); + if (!$e->getSourceContext()) { + $e->setSourceContext($this->stream->getSourceContext()); } if (!$e->getTemplateLine()) { @@ -111,7 +117,7 @@ class Twig_Parser implements Twig_ParserInterface throw $e; } - $node = new Twig_Node_Module(new Twig_Node_Body(array($body)), $this->parent, new Twig_Node($this->blocks), new Twig_Node($this->macros), new Twig_Node($this->traits), $this->embeddedTemplates, $this->getFilename()); + $node = new Twig_Node_Module(new Twig_Node_Body(array($body)), $this->parent, new Twig_Node($this->blocks), new Twig_Node($this->macros), new Twig_Node($this->traits), $this->embeddedTemplates, $stream->getSourceContext()); $traverser = new Twig_NodeTraverser($this->env, $this->visitors); @@ -148,7 +154,7 @@ class Twig_Parser implements Twig_ParserInterface $token = $this->getCurrentToken(); if ($token->getType() !== Twig_Token::NAME_TYPE) { - throw new Twig_Error_Syntax('A block must start with a tag name', $token->getLine(), $this->getFilename()); + throw new Twig_Error_Syntax('A block must start with a tag name.', $token->getLine(), $this->stream->getSourceContext()); } if (null !== $test && call_user_func($test, $token)) { @@ -166,20 +172,17 @@ class Twig_Parser implements Twig_ParserInterface $subparser = $this->handlers->getTokenParser($token->getValue()); if (null === $subparser) { if (null !== $test) { - $error = sprintf('Unexpected tag name "%s"', $token->getValue()); + $e = new Twig_Error_Syntax(sprintf('Unexpected "%s" tag', $token->getValue()), $token->getLine(), $this->stream->getSourceContext()); + if (is_array($test) && isset($test[0]) && $test[0] instanceof Twig_TokenParserInterface) { - $error .= sprintf(' (expecting closing tag for the "%s" tag defined near line %s)', $test[0]->getTag(), $lineno); + $e->appendMessage(sprintf(' (expecting closing tag for the "%s" tag defined near line %s).', $test[0]->getTag(), $lineno)); } - - throw new Twig_Error_Syntax($error, $token->getLine(), $this->getFilename()); + } else { + $e = new Twig_Error_Syntax(sprintf('Unknown "%s" tag.', $token->getValue()), $token->getLine(), $this->stream->getSourceContext()); + $e->addSuggestions($token->getValue(), array_keys($this->env->getTags())); } - $message = sprintf('Unknown tag name "%s"', $token->getValue()); - if ($alternatives = $this->env->computeAlternatives($token->getValue(), array_keys($this->env->getTags()))) { - $message = sprintf('%s. Did you mean "%s"', $message, implode('", "', $alternatives)); - } - - throw new Twig_Error_Syntax($message, $token->getLine(), $this->getFilename()); + throw $e; } $this->stream->next(); @@ -191,7 +194,7 @@ class Twig_Parser implements Twig_ParserInterface break; default: - throw new Twig_Error_Syntax('Lexer or parser ended up in unsupported state.', 0, $this->getFilename()); + throw new Twig_Error_Syntax('Lexer or parser ended up in unsupported state.', $this->getCurrentToken()->getLine(), $this->stream->getSourceContext()); } } @@ -202,13 +205,23 @@ class Twig_Parser implements Twig_ParserInterface return new Twig_Node($rv, array(), $lineno); } + /** + * @deprecated since 1.27 (to be removed in 2.0) + */ public function addHandler($name, $class) { + @trigger_error('The '.__METHOD__.' method is deprecated since version 1.27 and will be removed in 2.0.', E_USER_DEPRECATED); + $this->handlers[$name] = $class; } + /** + * @deprecated since 1.27 (to be removed in 2.0) + */ public function addNodeVisitor(Twig_NodeVisitorInterface $visitor) { + @trigger_error('The '.__METHOD__.' method is deprecated since version 1.27 and will be removed in 2.0.', E_USER_DEPRECATED); + $this->visitors[] = $visitor; } @@ -244,7 +257,7 @@ class Twig_Parser implements Twig_ParserInterface public function setBlock($name, Twig_Node_Block $value) { - $this->blocks[$name] = new Twig_Node_Body(array($value), array(), $value->getLine()); + $this->blocks[$name] = new Twig_Node_Body(array($value), array(), $value->getTemplateLine()); } public function hasMacro($name) @@ -255,7 +268,7 @@ class Twig_Parser implements Twig_ParserInterface public function setMacro($name, Twig_Node_Macro $node) { if ($this->isReservedMacroName($name)) { - throw new Twig_Error_Syntax(sprintf('"%s" cannot be used as a macro name as it is a reserved keyword', $name), $node->getLine(), $this->getFilename()); + throw new Twig_Error_Syntax(sprintf('"%s" cannot be used as a macro name as it is a reserved keyword.', $name), $node->getTemplateLine(), $this->stream->getSourceContext()); } $this->macros[$name] = $node; @@ -325,9 +338,7 @@ class Twig_Parser implements Twig_ParserInterface } /** - * Gets the expression parser. - * - * @return Twig_ExpressionParser The expression parser + * @return Twig_ExpressionParser */ public function getExpressionParser() { @@ -345,9 +356,7 @@ class Twig_Parser implements Twig_ParserInterface } /** - * Gets the token stream. - * - * @return Twig_TokenStream The token stream + * @return Twig_TokenStream */ public function getStream() { @@ -355,9 +364,7 @@ class Twig_Parser implements Twig_ParserInterface } /** - * Gets the current token. - * - * @return Twig_Token The current token + * @return Twig_Token */ public function getCurrentToken() { @@ -373,14 +380,14 @@ class Twig_Parser implements Twig_ParserInterface (!$node instanceof Twig_Node_Text && !$node instanceof Twig_Node_BlockReference && $node instanceof Twig_NodeOutputInterface) ) { if (false !== strpos((string) $node, chr(0xEF).chr(0xBB).chr(0xBF))) { - throw new Twig_Error_Syntax('A template that extends another one cannot have a body but a byte order mark (BOM) has been detected; it must be removed.', $node->getLine(), $this->getFilename()); + throw new Twig_Error_Syntax('A template that extends another one cannot start with a byte order mark (BOM); it must be removed.', $node->getTemplateLine(), $this->stream->getSourceContext()); } - throw new Twig_Error_Syntax('A template that extends another one cannot have a body.', $node->getLine(), $this->getFilename()); + throw new Twig_Error_Syntax('A template that extends another one cannot include contents outside Twig blocks. Did you forget to put the contents inside a {% block %} tag?', $node->getTemplateLine(), $this->stream->getSourceContext()); } - // bypass "set" nodes as they "capture" the output - if ($node instanceof Twig_Node_Set) { + // bypass nodes that will "capture" the output + if ($node instanceof Twig_NodeCaptureInterface) { return $node; } @@ -397,3 +404,7 @@ class Twig_Parser implements Twig_ParserInterface return $node; } } + +class_alias('Twig_Parser', 'Twig\Parser', false); +class_exists('Twig_Node'); +class_exists('Twig_TokenStream'); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/ParserInterface.php b/lib/silex/vendor/twig/twig/lib/Twig/ParserInterface.php index 8e7cc0a85..85c6e67b2 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/ParserInterface.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/ParserInterface.php @@ -3,7 +3,7 @@ /* * This file is part of Twig. * - * (c) 2009 Fabien Potencier + * (c) Fabien Potencier * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -21,9 +21,7 @@ interface Twig_ParserInterface /** * Converts a token stream to a node tree. * - * @param Twig_TokenStream $stream A token stream instance - * - * @return Twig_Node_Module A node tree + * @return Twig_Node_Module * * @throws Twig_Error_Syntax When the token stream is syntactically or semantically wrong */ diff --git a/lib/silex/vendor/twig/twig/lib/Twig/Profiler/Dumper/Base.php b/lib/silex/vendor/twig/twig/lib/Twig/Profiler/Dumper/Base.php new file mode 100644 index 000000000..913afd4f0 --- /dev/null +++ b/lib/silex/vendor/twig/twig/lib/Twig/Profiler/Dumper/Base.php @@ -0,0 +1,62 @@ + + */ +abstract class Twig_Profiler_Dumper_Base +{ + private $root; + + public function dump(Twig_Profiler_Profile $profile) + { + return $this->dumpProfile($profile); + } + + abstract protected function formatTemplate(Twig_Profiler_Profile $profile, $prefix); + + abstract protected function formatNonTemplate(Twig_Profiler_Profile $profile, $prefix); + + abstract protected function formatTime(Twig_Profiler_Profile $profile, $percent); + + private function dumpProfile(Twig_Profiler_Profile $profile, $prefix = '', $sibling = false) + { + if ($profile->isRoot()) { + $this->root = $profile->getDuration(); + $start = $profile->getName(); + } else { + if ($profile->isTemplate()) { + $start = $this->formatTemplate($profile, $prefix); + } else { + $start = $this->formatNonTemplate($profile, $prefix); + } + $prefix .= $sibling ? '│ ' : ' '; + } + + $percent = $this->root ? $profile->getDuration() / $this->root * 100 : 0; + + if ($profile->getDuration() * 1000 < 1) { + $str = $start."\n"; + } else { + $str = sprintf("%s %s\n", $start, $this->formatTime($profile, $percent)); + } + + $nCount = count($profile->getProfiles()); + foreach ($profile as $i => $p) { + $str .= $this->dumpProfile($p, $prefix, $i + 1 !== $nCount); + } + + return $str; + } +} + +class_alias('Twig_Profiler_Dumper_Base', 'Twig\Profiler\Dumper\BaseDumper', false); +class_exists('Twig_Profiler_Profile'); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/Profiler/Dumper/Blackfire.php b/lib/silex/vendor/twig/twig/lib/Twig/Profiler/Dumper/Blackfire.php index b82747a94..7a33baf2d 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/Profiler/Dumper/Blackfire.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/Profiler/Dumper/Blackfire.php @@ -3,7 +3,7 @@ /* * This file is part of Twig. * - * (c) 2015 Fabien Potencier + * (c) Fabien Potencier * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -11,6 +11,8 @@ /** * @author Fabien Potencier + * + * @final */ class Twig_Profiler_Dumper_Blackfire { @@ -20,7 +22,7 @@ class Twig_Profiler_Dumper_Blackfire $this->dumpProfile('main()', $profile, $data); $this->dumpChildren('main()', $profile, $data); - $start = microtime(true); + $start = sprintf('%f', microtime(true)); $str = << + * + * @final */ -class Twig_Profiler_Dumper_Html extends Twig_Profiler_Dumper_Text +class Twig_Profiler_Dumper_Html extends Twig_Profiler_Dumper_Base { private static $colors = array( 'block' => '#dfd', @@ -41,3 +43,5 @@ class Twig_Profiler_Dumper_Html extends Twig_Profiler_Dumper_Text return sprintf('%.2fms/%.0f%%', $percent > 20 ? self::$colors['big'] : 'auto', $profile->getDuration() * 1000, $percent); } } + +class_alias('Twig_Profiler_Dumper_Html', 'Twig\Profiler\Dumper\HtmlDumper', false); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/Profiler/Dumper/Text.php b/lib/silex/vendor/twig/twig/lib/Twig/Profiler/Dumper/Text.php index 998e210d3..69d2c4bf3 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/Profiler/Dumper/Text.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/Profiler/Dumper/Text.php @@ -3,7 +3,7 @@ /* * This file is part of Twig. * - * (c) 2015 Fabien Potencier + * (c) Fabien Potencier * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -11,16 +11,11 @@ /** * @author Fabien Potencier + * + * @final */ -class Twig_Profiler_Dumper_Text +class Twig_Profiler_Dumper_Text extends Twig_Profiler_Dumper_Base { - private $root; - - public function dump(Twig_Profiler_Profile $profile) - { - return $this->dumpProfile($profile); - } - protected function formatTemplate(Twig_Profiler_Profile $profile, $prefix) { return sprintf('%s└ %s', $prefix, $profile->getTemplate()); @@ -35,34 +30,6 @@ class Twig_Profiler_Dumper_Text { return sprintf('%.2fms/%.0f%%', $profile->getDuration() * 1000, $percent); } - - private function dumpProfile(Twig_Profiler_Profile $profile, $prefix = '', $sibling = false) - { - if ($profile->isRoot()) { - $this->root = $profile->getDuration(); - $start = $profile->getName(); - } else { - if ($profile->isTemplate()) { - $start = $this->formatTemplate($profile, $prefix); - } else { - $start = $this->formatNonTemplate($profile, $prefix); - } - $prefix .= $sibling ? '│ ' : ' '; - } - - $percent = $this->root ? $profile->getDuration() / $this->root * 100 : 0; - - if ($profile->getDuration() * 1000 < 1) { - $str = $start."\n"; - } else { - $str = sprintf("%s %s\n", $start, $this->formatTime($profile, $percent)); - } - - $nCount = count($profile->getProfiles()); - foreach ($profile as $i => $p) { - $str .= $this->dumpProfile($p, $prefix, $i + 1 !== $nCount); - } - - return $str; - } } + +class_alias('Twig_Profiler_Dumper_Text', 'Twig\Profiler\Dumper\TextDumper', false); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/Profiler/Node/EnterProfile.php b/lib/silex/vendor/twig/twig/lib/Twig/Profiler/Node/EnterProfile.php index 2f9721481..69c8f7970 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/Profiler/Node/EnterProfile.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/Profiler/Node/EnterProfile.php @@ -3,7 +3,7 @@ /* * This file is part of Twig. * - * (c) 2015 Fabien Potencier + * (c) Fabien Potencier * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -21,9 +21,6 @@ class Twig_Profiler_Node_EnterProfile extends Twig_Node parent::__construct(array(), array('extension_name' => $extensionName, 'name' => $name, 'type' => $type, 'var_name' => $varName)); } - /** - * {@inheritdoc} - */ public function compile(Twig_Compiler $compiler) { $compiler @@ -38,3 +35,5 @@ class Twig_Profiler_Node_EnterProfile extends Twig_Node ; } } + +class_alias('Twig_Profiler_Node_EnterProfile', 'Twig\Profiler\Node\EnterProfileNode', false); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/Profiler/Node/LeaveProfile.php b/lib/silex/vendor/twig/twig/lib/Twig/Profiler/Node/LeaveProfile.php index 88074c2f6..d1d6a7cc1 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/Profiler/Node/LeaveProfile.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/Profiler/Node/LeaveProfile.php @@ -3,7 +3,7 @@ /* * This file is part of Twig. * - * (c) 2015 Fabien Potencier + * (c) Fabien Potencier * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -21,9 +21,6 @@ class Twig_Profiler_Node_LeaveProfile extends Twig_Node parent::__construct(array(), array('var_name' => $varName)); } - /** - * {@inheritdoc} - */ public function compile(Twig_Compiler $compiler) { $compiler @@ -32,3 +29,5 @@ class Twig_Profiler_Node_LeaveProfile extends Twig_Node ; } } + +class_alias('Twig_Profiler_Node_LeaveProfile', 'Twig\Profiler\Node\LeaveProfileNode', false); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/Profiler/NodeVisitor/Profiler.php b/lib/silex/vendor/twig/twig/lib/Twig/Profiler/NodeVisitor/Profiler.php index 4b0baa82e..a395ae7fd 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/Profiler/NodeVisitor/Profiler.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/Profiler/NodeVisitor/Profiler.php @@ -3,7 +3,7 @@ /* * This file is part of Twig. * - * (c) 2015 Fabien Potencier + * (c) Fabien Potencier * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -11,6 +11,8 @@ /** * @author Fabien Potencier + * + * @final */ class Twig_Profiler_NodeVisitor_Profiler extends Twig_BaseNodeVisitor { @@ -21,22 +23,16 @@ class Twig_Profiler_NodeVisitor_Profiler extends Twig_BaseNodeVisitor $this->extensionName = $extensionName; } - /** - * {@inheritdoc} - */ protected function doEnterNode(Twig_Node $node, Twig_Environment $env) { return $node; } - /** - * {@inheritdoc} - */ protected function doLeaveNode(Twig_Node $node, Twig_Environment $env) { if ($node instanceof Twig_Node_Module) { $varName = $this->getVarName(); - $node->setNode('display_start', new Twig_Node(array(new Twig_Profiler_Node_EnterProfile($this->extensionName, Twig_Profiler_Profile::TEMPLATE, $node->getAttribute('filename'), $varName), $node->getNode('display_start')))); + $node->setNode('display_start', new Twig_Node(array(new Twig_Profiler_Node_EnterProfile($this->extensionName, Twig_Profiler_Profile::TEMPLATE, $node->getTemplateName(), $varName), $node->getNode('display_start')))); $node->setNode('display_end', new Twig_Node(array(new Twig_Profiler_Node_LeaveProfile($varName), $node->getNode('display_end')))); } elseif ($node instanceof Twig_Node_Block) { $varName = $this->getVarName(); @@ -62,11 +58,10 @@ class Twig_Profiler_NodeVisitor_Profiler extends Twig_BaseNodeVisitor return sprintf('__internal_%s', hash('sha256', uniqid(mt_rand(), true), false)); } - /** - * {@inheritdoc} - */ public function getPriority() { return 0; } } + +class_alias('Twig_Profiler_NodeVisitor_Profiler', 'Twig\Profiler\NodeVisitor\ProfilerNodeVisitor', false); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/Profiler/Profile.php b/lib/silex/vendor/twig/twig/lib/Twig/Profiler/Profile.php index ec9e25459..3fdc1a8ae 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/Profiler/Profile.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/Profiler/Profile.php @@ -3,7 +3,7 @@ /* * This file is part of Twig. * - * (c) 2015 Fabien Potencier + * (c) Fabien Potencier * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -11,6 +11,8 @@ /** * @author Fabien Potencier + * + * @final */ class Twig_Profiler_Profile implements IteratorAggregate, Serializable { @@ -86,6 +88,16 @@ class Twig_Profiler_Profile implements IteratorAggregate, Serializable */ public function getDuration() { + if ($this->isRoot() && $this->profiles) { + // for the root node with children, duration is the sum of all child durations + $duration = 0; + foreach ($this->profiles as $profile) { + $duration += $profile->getDuration(); + } + + return $duration; + } + return isset($this->ends['wt']) && isset($this->starts['wt']) ? $this->ends['wt'] - $this->starts['wt'] : 0; } @@ -133,6 +145,12 @@ class Twig_Profiler_Profile implements IteratorAggregate, Serializable ); } + public function reset() + { + $this->starts = $this->ends = $this->profiles = array(); + $this->enter(); + } + public function getIterator() { return new ArrayIterator($this->profiles); @@ -148,3 +166,5 @@ class Twig_Profiler_Profile implements IteratorAggregate, Serializable list($this->template, $this->name, $this->type, $this->starts, $this->ends, $this->profiles) = unserialize($data); } } + +class_alias('Twig_Profiler_Profile', 'Twig\Profiler\Profile', false); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/RuntimeLoaderInterface.php b/lib/silex/vendor/twig/twig/lib/Twig/RuntimeLoaderInterface.php new file mode 100644 index 000000000..f5eb14e0d --- /dev/null +++ b/lib/silex/vendor/twig/twig/lib/Twig/RuntimeLoaderInterface.php @@ -0,0 +1,29 @@ + + */ +interface Twig_RuntimeLoaderInterface +{ + /** + * Creates the runtime implementation of a Twig element (filter/function/test). + * + * @param string $class A runtime class + * + * @return object|null The runtime instance or null if the loader does not know how to create the runtime for this class + */ + public function load($class); +} + +class_alias('Twig_RuntimeLoaderInterface', 'Twig\RuntimeLoader\RuntimeLoaderInterface', false); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/Sandbox/SecurityError.php b/lib/silex/vendor/twig/twig/lib/Twig/Sandbox/SecurityError.php index 015bfaea2..b6707e386 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/Sandbox/SecurityError.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/Sandbox/SecurityError.php @@ -3,7 +3,7 @@ /* * This file is part of Twig. * - * (c) 2009 Fabien Potencier + * (c) Fabien Potencier * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -17,3 +17,5 @@ class Twig_Sandbox_SecurityError extends Twig_Error { } + +class_alias('Twig_Sandbox_SecurityError', 'Twig\Sandbox\SecurityError', false); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/Sandbox/SecurityNotAllowedFilterError.php b/lib/silex/vendor/twig/twig/lib/Twig/Sandbox/SecurityNotAllowedFilterError.php index 99faba9dd..0ba332762 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/Sandbox/SecurityNotAllowedFilterError.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/Sandbox/SecurityNotAllowedFilterError.php @@ -3,7 +3,7 @@ /* * This file is part of Twig. * - * (c) 2009 Fabien Potencier + * (c) Fabien Potencier * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -29,3 +29,5 @@ class Twig_Sandbox_SecurityNotAllowedFilterError extends Twig_Sandbox_SecurityEr return $this->filterName; } } + +class_alias('Twig_Sandbox_SecurityNotAllowedFilterError', 'Twig\Sandbox\SecurityNotAllowedFilterError', false); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/Sandbox/SecurityNotAllowedFunctionError.php b/lib/silex/vendor/twig/twig/lib/Twig/Sandbox/SecurityNotAllowedFunctionError.php index 05cf488af..aa3914293 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/Sandbox/SecurityNotAllowedFunctionError.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/Sandbox/SecurityNotAllowedFunctionError.php @@ -3,7 +3,7 @@ /* * This file is part of Twig. * - * (c) 2009 Fabien Potencier + * (c) Fabien Potencier * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -29,3 +29,5 @@ class Twig_Sandbox_SecurityNotAllowedFunctionError extends Twig_Sandbox_Security return $this->functionName; } } + +class_alias('Twig_Sandbox_SecurityNotAllowedFunctionError', 'Twig\Sandbox\SecurityNotAllowedFunctionError', false); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/Sandbox/SecurityNotAllowedMethodError.php b/lib/silex/vendor/twig/twig/lib/Twig/Sandbox/SecurityNotAllowedMethodError.php new file mode 100644 index 000000000..93012fe99 --- /dev/null +++ b/lib/silex/vendor/twig/twig/lib/Twig/Sandbox/SecurityNotAllowedMethodError.php @@ -0,0 +1,40 @@ + + */ +class Twig_Sandbox_SecurityNotAllowedMethodError extends Twig_Sandbox_SecurityError +{ + private $className; + private $methodName; + + public function __construct($message, $className, $methodName, $lineno = -1, $filename = null, Exception $previous = null) + { + parent::__construct($message, $lineno, $filename, $previous); + $this->className = $className; + $this->methodName = $methodName; + } + + public function getClassName() + { + return $this->className; + } + + public function getMethodName() + { + return $this->methodName; + } +} + +class_alias('Twig_Sandbox_SecurityNotAllowedMethodError', 'Twig\Sandbox\SecurityNotAllowedMethodError', false); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/Sandbox/SecurityNotAllowedPropertyError.php b/lib/silex/vendor/twig/twig/lib/Twig/Sandbox/SecurityNotAllowedPropertyError.php new file mode 100644 index 000000000..f27969c15 --- /dev/null +++ b/lib/silex/vendor/twig/twig/lib/Twig/Sandbox/SecurityNotAllowedPropertyError.php @@ -0,0 +1,40 @@ + + */ +class Twig_Sandbox_SecurityNotAllowedPropertyError extends Twig_Sandbox_SecurityError +{ + private $className; + private $propertyName; + + public function __construct($message, $className, $propertyName, $lineno = -1, $filename = null, Exception $previous = null) + { + parent::__construct($message, $lineno, $filename, $previous); + $this->className = $className; + $this->propertyName = $propertyName; + } + + public function getClassName() + { + return $this->className; + } + + public function getPropertyName() + { + return $this->propertyName; + } +} + +class_alias('Twig_Sandbox_SecurityNotAllowedPropertyError', 'Twig\Sandbox\SecurityNotAllowedPropertyError', false); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/Sandbox/SecurityNotAllowedTagError.php b/lib/silex/vendor/twig/twig/lib/Twig/Sandbox/SecurityNotAllowedTagError.php index b3bb5e8e2..4bbd22387 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/Sandbox/SecurityNotAllowedTagError.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/Sandbox/SecurityNotAllowedTagError.php @@ -3,7 +3,7 @@ /* * This file is part of Twig. * - * (c) 2009 Fabien Potencier + * (c) Fabien Potencier * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -29,3 +29,5 @@ class Twig_Sandbox_SecurityNotAllowedTagError extends Twig_Sandbox_SecurityError return $this->tagName; } } + +class_alias('Twig_Sandbox_SecurityNotAllowedTagError', 'Twig\Sandbox\SecurityNotAllowedTagError', false); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/Sandbox/SecurityPolicy.php b/lib/silex/vendor/twig/twig/lib/Twig/Sandbox/SecurityPolicy.php index c4dd03dff..dca0b82b5 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/Sandbox/SecurityPolicy.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/Sandbox/SecurityPolicy.php @@ -3,7 +3,7 @@ /* * This file is part of Twig. * - * (c) 2009 Fabien Potencier + * (c) Fabien Potencier * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -12,6 +12,8 @@ /** * Represents a security policy which need to be enforced when sandbox mode is enabled. * + * @final + * * @author Fabien Potencier */ class Twig_Sandbox_SecurityPolicy implements Twig_Sandbox_SecurityPolicyInterface @@ -97,7 +99,8 @@ class Twig_Sandbox_SecurityPolicy implements Twig_Sandbox_SecurityPolicyInterfac } if (!$allowed) { - throw new Twig_Sandbox_SecurityError(sprintf('Calling "%s" method on a "%s" object is not allowed.', $method, get_class($obj))); + $class = get_class($obj); + throw new Twig_Sandbox_SecurityNotAllowedMethodError(sprintf('Calling "%s" method on a "%s" object is not allowed.', $method, $class), $class, $method); } } @@ -113,7 +116,10 @@ class Twig_Sandbox_SecurityPolicy implements Twig_Sandbox_SecurityPolicyInterfac } if (!$allowed) { - throw new Twig_Sandbox_SecurityError(sprintf('Calling "%s" property on a "%s" object is not allowed.', $property, get_class($obj))); + $class = get_class($obj); + throw new Twig_Sandbox_SecurityNotAllowedPropertyError(sprintf('Calling "%s" property on a "%s" object is not allowed.', $property, $class), $class, $property); } } } + +class_alias('Twig_Sandbox_SecurityPolicy', 'Twig\Sandbox\SecurityPolicy', false); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/Sandbox/SecurityPolicyInterface.php b/lib/silex/vendor/twig/twig/lib/Twig/Sandbox/SecurityPolicyInterface.php index 6ab48e3cc..88f64447a 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/Sandbox/SecurityPolicyInterface.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/Sandbox/SecurityPolicyInterface.php @@ -3,7 +3,7 @@ /* * This file is part of Twig. * - * (c) 2009 Fabien Potencier + * (c) Fabien Potencier * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -22,3 +22,5 @@ interface Twig_Sandbox_SecurityPolicyInterface public function checkPropertyAllowed($obj, $method); } + +class_alias('Twig_Sandbox_SecurityPolicyInterface', 'Twig\Sandbox\SecurityPolicyInterface', false); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/SimpleFilter.php b/lib/silex/vendor/twig/twig/lib/Twig/SimpleFilter.php index cefc4f587..ee4c0aebf 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/SimpleFilter.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/SimpleFilter.php @@ -3,7 +3,7 @@ /* * This file is part of Twig. * - * (c) 2009-2012 Fabien Potencier + * (c) Fabien Potencier * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -12,6 +12,8 @@ /** * Represents a template filter. * + * @final + * * @author Fabien Potencier */ class Twig_SimpleFilter @@ -101,6 +103,11 @@ class Twig_SimpleFilter } public function isDeprecated() + { + return (bool) $this->options['deprecated']; + } + + public function getDeprecatedVersion() { return $this->options['deprecated']; } @@ -110,3 +117,5 @@ class Twig_SimpleFilter return $this->options['alternative']; } } + +class_alias('Twig_SimpleFilter', 'Twig\TwigFilter', false); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/SimpleFunction.php b/lib/silex/vendor/twig/twig/lib/Twig/SimpleFunction.php index 79735405c..a6aa7ca74 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/SimpleFunction.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/SimpleFunction.php @@ -3,7 +3,7 @@ /* * This file is part of Twig. * - * (c) 2010-2012 Fabien Potencier + * (c) Fabien Potencier * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -12,6 +12,8 @@ /** * Represents a template function. * + * @final + * * @author Fabien Potencier */ class Twig_SimpleFunction @@ -91,6 +93,11 @@ class Twig_SimpleFunction } public function isDeprecated() + { + return (bool) $this->options['deprecated']; + } + + public function getDeprecatedVersion() { return $this->options['deprecated']; } @@ -100,3 +107,5 @@ class Twig_SimpleFunction return $this->options['alternative']; } } + +class_alias('Twig_SimpleFunction', 'Twig\TwigFunction', false); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/SimpleTest.php b/lib/silex/vendor/twig/twig/lib/Twig/SimpleTest.php index 8ba2192b2..fee5778b3 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/SimpleTest.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/SimpleTest.php @@ -3,7 +3,7 @@ /* * This file is part of Twig. * - * (c) 2010-2012 Fabien Potencier + * (c) Fabien Potencier * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -12,6 +12,8 @@ /** * Represents a template test. * + * @final + * * @author Fabien Potencier */ class Twig_SimpleTest @@ -53,6 +55,11 @@ class Twig_SimpleTest } public function isDeprecated() + { + return (bool) $this->options['deprecated']; + } + + public function getDeprecatedVersion() { return $this->options['deprecated']; } @@ -62,3 +69,5 @@ class Twig_SimpleTest return $this->options['alternative']; } } + +class_alias('Twig_SimpleTest', 'Twig\TwigTest', false); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/Source.php b/lib/silex/vendor/twig/twig/lib/Twig/Source.php new file mode 100644 index 000000000..bd8d869f3 --- /dev/null +++ b/lib/silex/vendor/twig/twig/lib/Twig/Source.php @@ -0,0 +1,53 @@ + + */ +class Twig_Source +{ + private $code; + private $name; + private $path; + + /** + * @param string $code The template source code + * @param string $name The template logical name + * @param string $path The filesystem path of the template if any + */ + public function __construct($code, $name, $path = '') + { + $this->code = $code; + $this->name = $name; + $this->path = $path; + } + + public function getCode() + { + return $this->code; + } + + public function getName() + { + return $this->name; + } + + public function getPath() + { + return $this->path; + } +} + +class_alias('Twig_Source', 'Twig\Source', false); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/SourceContextLoaderInterface.php b/lib/silex/vendor/twig/twig/lib/Twig/SourceContextLoaderInterface.php new file mode 100644 index 000000000..a6e8c4255 --- /dev/null +++ b/lib/silex/vendor/twig/twig/lib/Twig/SourceContextLoaderInterface.php @@ -0,0 +1,33 @@ + + * + * @deprecated since 1.27 (to be removed in 3.0) + */ +interface Twig_SourceContextLoaderInterface +{ + /** + * Returns the source context for a given template logical name. + * + * @param string $name The template logical name + * + * @return Twig_Source + * + * @throws Twig_Error_Loader When $name is not found + */ + public function getSourceContext($name); +} + +class_alias('Twig_SourceContextLoaderInterface', 'Twig\Loader\SourceContextLoaderInterface', false); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/Template.php b/lib/silex/vendor/twig/twig/lib/Twig/Template.php index e2ab05a24..64563419e 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/Template.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/Template.php @@ -3,8 +3,8 @@ /* * This file is part of Twig. * - * (c) 2009 Fabien Potencier - * (c) 2009 Armin Ronacher + * (c) Fabien Potencier + * (c) Armin Ronacher * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,10 +13,19 @@ /** * Default base class for compiled templates. * + * This class is an implementation detail of how template compilation currently + * works, which might change. It should never be used directly. Use $twig->load() + * instead, which returns an instance of Twig_TemplateWrapper. + * * @author Fabien Potencier + * + * @internal */ abstract class Twig_Template implements Twig_TemplateInterface { + /** + * @internal + */ protected static $cache = array(); protected $parent; @@ -25,16 +34,19 @@ abstract class Twig_Template implements Twig_TemplateInterface protected $blocks = array(); protected $traits = array(); - /** - * Constructor. - * - * @param Twig_Environment $env A Twig_Environment instance - */ public function __construct(Twig_Environment $env) { $this->env = $env; } + /** + * @internal this method will be removed in 2.0 and is only used internally to provide an upgrade path from 1.x to 2.0 + */ + public function __toString() + { + return $this->getTemplateName(); + } + /** * Returns the template name. * @@ -42,6 +54,42 @@ abstract class Twig_Template implements Twig_TemplateInterface */ abstract public function getTemplateName(); + /** + * Returns debug information about the template. + * + * @return array Debug information + * + * @internal + */ + public function getDebugInfo() + { + return array(); + } + + /** + * Returns the template source code. + * + * @return string The template source code + * + * @deprecated since 1.27 (to be removed in 2.0). Use getSourceContext() instead + */ + public function getSource() + { + @trigger_error('The '.__METHOD__.' method is deprecated since version 1.27 and will be removed in 2.0. Use getSourceContext() instead.', E_USER_DEPRECATED); + + return ''; + } + + /** + * Returns information about the original template source code. + * + * @return Twig_Source + */ + public function getSourceContext() + { + return new Twig_Source('', $this->getTemplateName()); + } + /** * @deprecated since 1.20 (to be removed in 2.0) */ @@ -61,6 +109,8 @@ abstract class Twig_Template implements Twig_TemplateInterface * @param array $context * * @return Twig_TemplateInterface|false The parent template or false if there is no parent + * + * @internal */ public function getParent(array $context) { @@ -83,7 +133,7 @@ abstract class Twig_Template implements Twig_TemplateInterface $this->parents[$parent] = $this->loadTemplate($parent); } } catch (Twig_Error_Loader $e) { - $e->setTemplateFile(null); + $e->setSourceContext(null); $e->guess(); throw $e; @@ -111,6 +161,8 @@ abstract class Twig_Template implements Twig_TemplateInterface * @param string $name The block name to display from the parent * @param array $context The context * @param array $blocks The current set of blocks + * + * @internal */ public function displayParentBlock($name, array $context, array $blocks = array()) { @@ -121,7 +173,7 @@ abstract class Twig_Template implements Twig_TemplateInterface } elseif (false !== $parent = $this->getParent($context)) { $parent->displayBlock($name, $context, $blocks, false); } else { - throw new Twig_Error_Runtime(sprintf('The template has no parent and no traits defining the "%s" block', $name), -1, $this->getTemplateName()); + throw new Twig_Error_Runtime(sprintf('The template has no parent and no traits defining the "%s" block.', $name), -1, $this->getSourceContext()); } } @@ -135,6 +187,8 @@ abstract class Twig_Template implements Twig_TemplateInterface * @param array $context The context * @param array $blocks The current set of blocks * @param bool $useBlocks Whether to use the current set of blocks + * + * @internal */ public function displayBlock($name, array $context, array $blocks = array(), $useBlocks = true) { @@ -151,17 +205,17 @@ abstract class Twig_Template implements Twig_TemplateInterface $block = null; } - if (null !== $template) { - // avoid RCEs when sandbox is enabled - if (!$template instanceof self) { - throw new LogicException('A block must be a method on a Twig_Template instance.'); - } + // avoid RCEs when sandbox is enabled + if (null !== $template && !$template instanceof self) { + throw new LogicException('A block must be a method on a Twig_Template instance.'); + } + if (null !== $template) { try { $template->$block($context, $blocks); } catch (Twig_Error $e) { - if (!$e->getTemplateFile()) { - $e->setTemplateFile($template->getTemplateName()); + if (!$e->getSourceContext()) { + $e->setSourceContext($template->getSourceContext()); } // this is mostly useful for Twig_Error_Loader exceptions @@ -173,10 +227,12 @@ abstract class Twig_Template implements Twig_TemplateInterface throw $e; } catch (Exception $e) { - throw new Twig_Error_Runtime(sprintf('An exception has been thrown during the rendering of a template ("%s").', $e->getMessage()), -1, $template->getTemplateName(), $e); + throw new Twig_Error_Runtime(sprintf('An exception has been thrown during the rendering of a template ("%s").', $e->getMessage()), -1, $template->getSourceContext(), $e); } } elseif (false !== $parent = $this->getParent($context)) { $parent->displayBlock($name, $context, array_merge($this->blocks, $blocks), false); + } else { + @trigger_error(sprintf('Silent display of undefined block "%s" in template "%s" is deprecated since version 1.29 and will throw an exception in 2.0. Use the "block(\'%s\') is defined" expression to test for block existence.', $name, $this->getTemplateName(), $name), E_USER_DEPRECATED); } } @@ -191,6 +247,8 @@ abstract class Twig_Template implements Twig_TemplateInterface * @param array $blocks The current set of blocks * * @return string The rendered block + * + * @internal */ public function renderParentBlock($name, array $context, array $blocks = array()) { @@ -212,6 +270,8 @@ abstract class Twig_Template implements Twig_TemplateInterface * @param bool $useBlocks Whether to use the current set of blocks * * @return string The rendered block + * + * @internal */ public function renderBlock($name, array $context, array $blocks = array(), $useBlocks = true) { @@ -222,40 +282,70 @@ abstract class Twig_Template implements Twig_TemplateInterface } /** - * Returns whether a block exists or not. + * Returns whether a block exists or not in the current context of the template. * - * This method is for internal use only and should never be called - * directly. + * This method checks blocks defined in the current template + * or defined in "used" traits or defined in parent templates. * - * This method does only return blocks defined in the current template - * or defined in "used" traits. - * - * It does not return blocks from parent templates as the parent - * template name can be dynamic, which is only known based on the - * current context. - * - * @param string $name The block name + * @param string $name The block name + * @param array $context The context + * @param array $blocks The current set of blocks * * @return bool true if the block exists, false otherwise + * + * @internal */ - public function hasBlock($name) + public function hasBlock($name, array $context = null, array $blocks = array()) { - return isset($this->blocks[(string) $name]); + if (null === $context) { + @trigger_error('The '.__METHOD__.' method is internal and should never be called; calling it directly is deprecated since version 1.28 and won\'t be possible anymore in 2.0.', E_USER_DEPRECATED); + + return isset($this->blocks[(string) $name]); + } + + if (isset($blocks[$name])) { + return $blocks[$name][0] instanceof self; + } + + if (isset($this->blocks[$name])) { + return true; + } + + if (false !== $parent = $this->getParent($context)) { + return $parent->hasBlock($name, $context); + } + + return false; } /** - * Returns all block names. + * Returns all block names in the current context of the template. * - * This method is for internal use only and should never be called - * directly. + * This method checks blocks defined in the current template + * or defined in "used" traits or defined in parent templates. + * + * @param array $context The context + * @param array $blocks The current set of blocks * * @return array An array of block names * - * @see hasBlock + * @internal */ - public function getBlockNames() + public function getBlockNames(array $context = null, array $blocks = array()) { - return array_keys($this->blocks); + if (null === $context) { + @trigger_error('The '.__METHOD__.' method is internal and should never be called; calling it directly is deprecated since version 1.28 and won\'t be possible anymore in 2.0.', E_USER_DEPRECATED); + + return array_keys($this->blocks); + } + + $names = array_merge(array_keys($blocks), array_keys($this->blocks)); + + if (false !== $parent = $this->getParent($context)) { + $names = array_merge($names, $parent->getBlockNames($context)); + } + + return array_unique($names); } protected function loadTemplate($template, $templateName = null, $line = null, $index = null) @@ -269,10 +359,14 @@ abstract class Twig_Template implements Twig_TemplateInterface return $template; } + if ($template instanceof Twig_TemplateWrapper) { + return $template; + } + return $this->env->loadTemplate($template, $index); } catch (Twig_Error $e) { - if (!$e->getTemplateFile()) { - $e->setTemplateFile($templateName ? $templateName : $this->getTemplateName()); + if (!$e->getSourceContext()) { + $e->setSourceContext($templateName ? new Twig_Source('', $templateName) : $this->getSourceContext()); } if ($e->getTemplateLine()) { @@ -297,24 +391,18 @@ abstract class Twig_Template implements Twig_TemplateInterface * * @return array An array of blocks * - * @see hasBlock + * @internal */ public function getBlocks() { return $this->blocks; } - /** - * {@inheritdoc} - */ public function display(array $context, array $blocks = array()) { $this->displayWithErrorHandling($this->env->mergeGlobals($context), array_merge($this->blocks, $blocks)); } - /** - * {@inheritdoc} - */ public function render(array $context) { $level = ob_get_level(); @@ -326,6 +414,12 @@ abstract class Twig_Template implements Twig_TemplateInterface ob_end_clean(); } + throw $e; + } catch (Throwable $e) { + while (ob_get_level() > $level) { + ob_end_clean(); + } + throw $e; } @@ -337,8 +431,8 @@ abstract class Twig_Template implements Twig_TemplateInterface try { $this->doDisplay($context, $blocks); } catch (Twig_Error $e) { - if (!$e->getTemplateFile()) { - $e->setTemplateFile($this->getTemplateName()); + if (!$e->getSourceContext()) { + $e->setSourceContext($this->getSourceContext()); } // this is mostly useful for Twig_Error_Loader exceptions @@ -350,7 +444,7 @@ abstract class Twig_Template implements Twig_TemplateInterface throw $e; } catch (Exception $e) { - throw new Twig_Error_Runtime(sprintf('An exception has been thrown during the rendering of a template ("%s").', $e->getMessage()), -1, $this->getTemplateName(), $e); + throw new Twig_Error_Runtime(sprintf('An exception has been thrown during the rendering of a template ("%s").', $e->getMessage()), -1, $this->getSourceContext(), $e); } } @@ -380,6 +474,8 @@ abstract class Twig_Template implements Twig_TemplateInterface * @return mixed The content of the context variable * * @throws Twig_Error_Runtime if the variable does not exist and Twig is running in strict mode + * + * @internal */ final protected function getContext($context, $item, $ignoreStrictCheck = false) { @@ -388,7 +484,7 @@ abstract class Twig_Template implements Twig_TemplateInterface return; } - throw new Twig_Error_Runtime(sprintf('Variable "%s" does not exist', $item), -1, $this->getTemplateName()); + throw new Twig_Error_Runtime(sprintf('Variable "%s" does not exist.', $item), -1, $this->getSourceContext()); } return $context[$item]; @@ -407,6 +503,8 @@ abstract class Twig_Template implements Twig_TemplateInterface * @return mixed The attribute value, or a Boolean when $isDefinedTest is true, or null when the attribute is not set and $ignoreStrictCheck is true * * @throws Twig_Error_Runtime if the attribute does not exist and Twig is running in strict mode and $isDefinedTest is false + * + * @internal */ protected function getAttribute($object, $item, array $arguments = array(), $type = self::ANY_CALL, $isDefinedTest = false, $ignoreStrictCheck = false) { @@ -414,7 +512,7 @@ abstract class Twig_Template implements Twig_TemplateInterface if (self::METHOD_CALL !== $type) { $arrayItem = is_bool($item) || is_float($item) ? (int) $item : $item; - if ((is_array($object) && array_key_exists($arrayItem, $object)) + if ((is_array($object) && (isset($object[$arrayItem]) || array_key_exists($arrayItem, $object))) || ($object instanceof ArrayAccess && isset($object[$arrayItem])) ) { if ($isDefinedTest) { @@ -434,28 +532,28 @@ abstract class Twig_Template implements Twig_TemplateInterface } if ($object instanceof ArrayAccess) { - $message = sprintf('Key "%s" in object with ArrayAccess of class "%s" does not exist', $arrayItem, get_class($object)); + $message = sprintf('Key "%s" in object with ArrayAccess of class "%s" does not exist.', $arrayItem, get_class($object)); } elseif (is_object($object)) { - $message = sprintf('Impossible to access a key "%s" on an object of class "%s" that does not implement ArrayAccess interface', $item, get_class($object)); + $message = sprintf('Impossible to access a key "%s" on an object of class "%s" that does not implement ArrayAccess interface.', $item, get_class($object)); } elseif (is_array($object)) { if (empty($object)) { - $message = sprintf('Key "%s" does not exist as the array is empty', $arrayItem); + $message = sprintf('Key "%s" does not exist as the array is empty.', $arrayItem); } else { - $message = sprintf('Key "%s" for array with keys "%s" does not exist', $arrayItem, implode(', ', array_keys($object))); + $message = sprintf('Key "%s" for array with keys "%s" does not exist.', $arrayItem, implode(', ', array_keys($object))); } } elseif (self::ARRAY_CALL === $type) { if (null === $object) { - $message = sprintf('Impossible to access a key ("%s") on a null variable', $item); + $message = sprintf('Impossible to access a key ("%s") on a null variable.', $item); } else { - $message = sprintf('Impossible to access a key ("%s") on a %s variable ("%s")', $item, gettype($object), $object); + $message = sprintf('Impossible to access a key ("%s") on a %s variable ("%s").', $item, gettype($object), $object); } } elseif (null === $object) { - $message = sprintf('Impossible to access an attribute ("%s") on a null variable', $item); + $message = sprintf('Impossible to access an attribute ("%s") on a null variable.', $item); } else { - $message = sprintf('Impossible to access an attribute ("%s") on a %s variable ("%s")', $item, gettype($object), $object); + $message = sprintf('Impossible to access an attribute ("%s") on a %s variable ("%s").', $item, gettype($object), $object); } - throw new Twig_Error_Runtime($message, -1, $this->getTemplateName()); + throw new Twig_Error_Runtime($message, -1, $this->getSourceContext()); } } @@ -469,12 +567,12 @@ abstract class Twig_Template implements Twig_TemplateInterface } if (null === $object) { - $message = sprintf('Impossible to invoke a method ("%s") on a null variable', $item); + $message = sprintf('Impossible to invoke a method ("%s") on a null variable.', $item); } else { - $message = sprintf('Impossible to invoke a method ("%s") on a %s variable ("%s")', $item, gettype($object), $object); + $message = sprintf('Impossible to invoke a method ("%s") on a %s variable ("%s").', $item, gettype($object), $object); } - throw new Twig_Error_Runtime($message, -1, $this->getTemplateName()); + throw new Twig_Error_Runtime($message, -1, $this->getSourceContext()); } // object property @@ -484,8 +582,8 @@ abstract class Twig_Template implements Twig_TemplateInterface return true; } - if ($this->env->hasExtension('sandbox')) { - $this->env->getExtension('sandbox')->checkPropertyAllowed($object, $item); + if ($this->env->hasExtension('Twig_Extension_Sandbox')) { + $this->env->getExtension('Twig_Extension_Sandbox')->checkPropertyAllowed($object, $item); } return $object->$item; @@ -495,37 +593,60 @@ abstract class Twig_Template implements Twig_TemplateInterface $class = get_class($object); // object method - if (!isset(self::$cache[$class]['methods'])) { + if (!isset(self::$cache[$class])) { // get_class_methods returns all methods accessible in the scope, but we only want public ones to be accessible in templates if ($object instanceof self) { $ref = new ReflectionClass($class); $methods = array(); foreach ($ref->getMethods(ReflectionMethod::IS_PUBLIC) as $refMethod) { - $methodName = strtolower($refMethod->name); - // Accessing the environment from templates is forbidden to prevent untrusted changes to the environment - if ('getenvironment' !== $methodName) { - $methods[$methodName] = true; + if ('getenvironment' !== strtolower($refMethod->name)) { + $methods[] = $refMethod->name; } } - - self::$cache[$class]['methods'] = $methods; } else { - self::$cache[$class]['methods'] = array_change_key_case(array_flip(get_class_methods($object))); + $methods = get_class_methods($object); } + // sort values to have consistent behavior, so that "get" methods win precedence over "is" methods + sort($methods); + + $cache = array(); + + foreach ($methods as $method) { + $cache[$method] = $method; + $cache[$lcName = strtolower($method)] = $method; + + if ('g' === $lcName[0] && 0 === strpos($lcName, 'get')) { + $name = substr($method, 3); + $lcName = substr($lcName, 3); + } elseif ('i' === $lcName[0] && 0 === strpos($lcName, 'is')) { + $name = substr($method, 2); + $lcName = substr($lcName, 2); + } else { + continue; + } + + // skip get() and is() methods (in which case, $name is empty) + if ($name) { + if (!isset($cache[$name])) { + $cache[$name] = $method; + } + if (!isset($cache[$lcName])) { + $cache[$lcName] = $method; + } + } + } + self::$cache[$class] = $cache; } $call = false; - $lcItem = strtolower($item); - if (isset(self::$cache[$class]['methods'][$lcItem])) { - $method = (string) $item; - } elseif (isset(self::$cache[$class]['methods']['get'.$lcItem])) { - $method = 'get'.$item; - } elseif (isset(self::$cache[$class]['methods']['is'.$lcItem])) { - $method = 'is'.$item; - } elseif (isset(self::$cache[$class]['methods']['__call'])) { - $method = (string) $item; + if (isset(self::$cache[$class][$item])) { + $method = self::$cache[$class][$item]; + } elseif (isset(self::$cache[$class][$lcItem = strtolower($item)])) { + $method = self::$cache[$class][$lcItem]; + } elseif (isset(self::$cache[$class]['__call'])) { + $method = $item; $call = true; } else { if ($isDefinedTest) { @@ -536,21 +657,25 @@ abstract class Twig_Template implements Twig_TemplateInterface return; } - throw new Twig_Error_Runtime(sprintf('Method "%s" for object "%s" does not exist', $item, get_class($object)), -1, $this->getTemplateName()); + throw new Twig_Error_Runtime(sprintf('Neither the property "%1$s" nor one of the methods "%1$s()", "get%1$s()"/"is%1$s()" or "__call()" exist and have public access in class "%2$s".', $item, $class), -1, $this->getSourceContext()); } if ($isDefinedTest) { return true; } - if ($this->env->hasExtension('sandbox')) { - $this->env->getExtension('sandbox')->checkMethodAllowed($object, $method); + if ($this->env->hasExtension('Twig_Extension_Sandbox')) { + $this->env->getExtension('Twig_Extension_Sandbox')->checkMethodAllowed($object, $method); } // Some objects throw exceptions when they have __call, and the method we try // to call is not supported. If ignoreStrictCheck is true, we should return null. try { - $ret = call_user_func_array(array($object, $method), $arguments); + if (!$arguments) { + $ret = $object->$method(); + } else { + $ret = call_user_func_array(array($object, $method), $arguments); + } } catch (BadMethodCallException $e) { if ($call && ($ignoreStrictCheck || !$this->env->isStrictVariables())) { return; @@ -558,12 +683,24 @@ abstract class Twig_Template implements Twig_TemplateInterface throw $e; } - // useful when calling a template method from a template - // this is not supported but unfortunately heavily used in the Symfony profiler + // @deprecated in 1.28 if ($object instanceof Twig_TemplateInterface) { + $self = $object->getTemplateName() === $this->getTemplateName(); + $message = sprintf('Calling "%s" on template "%s" from template "%s" is deprecated since version 1.28 and won\'t be supported anymore in 2.0.', $item, $object->getTemplateName(), $this->getTemplateName()); + if ('renderBlock' === $method || 'displayBlock' === $method) { + $message .= sprintf(' Use block("%s"%s) instead).', $arguments[0], $self ? '' : ', template'); + } elseif ('hasBlock' === $method) { + $message .= sprintf(' Use "block("%s"%s) is defined" instead).', $arguments[0], $self ? '' : ', template'); + } elseif ('render' === $method || 'display' === $method) { + $message .= sprintf(' Use include("%s") instead).', $object->getTemplateName()); + } + @trigger_error($message, E_USER_DEPRECATED); + return $ret === '' ? '' : new Twig_Markup($ret, $this->env->getCharset()); } return $ret; } } + +class_alias('Twig_Template', 'Twig\Template', false); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/TemplateInterface.php b/lib/silex/vendor/twig/twig/lib/Twig/TemplateInterface.php index 32746407e..457ef7d7b 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/TemplateInterface.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/TemplateInterface.php @@ -3,7 +3,7 @@ /* * This file is part of Twig. * - * (c) 2009 Fabien Potencier + * (c) Fabien Potencier * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -42,7 +42,7 @@ interface Twig_TemplateInterface /** * Returns the bound environment for this template. * - * @return Twig_Environment The current environment + * @return Twig_Environment */ public function getEnvironment(); } diff --git a/lib/silex/vendor/twig/twig/lib/Twig/TemplateWrapper.php b/lib/silex/vendor/twig/twig/lib/Twig/TemplateWrapper.php new file mode 100644 index 000000000..497f6e980 --- /dev/null +++ b/lib/silex/vendor/twig/twig/lib/Twig/TemplateWrapper.php @@ -0,0 +1,133 @@ + + */ +final class Twig_TemplateWrapper +{ + private $env; + private $template; + + /** + * This method is for internal use only and should never be called + * directly (use Twig_Environment::load() instead). + * + * @internal + */ + public function __construct(Twig_Environment $env, Twig_Template $template) + { + $this->env = $env; + $this->template = $template; + } + + /** + * Renders the template. + * + * @param array $context An array of parameters to pass to the template + * + * @return string The rendered template + */ + public function render($context = array()) + { + return $this->template->render($context); + } + + /** + * Displays the template. + * + * @param array $context An array of parameters to pass to the template + */ + public function display($context = array()) + { + $this->template->display($context); + } + + /** + * Checks if a block is defined. + * + * @param string $name The block name + * @param array $context An array of parameters to pass to the template + * + * @return bool + */ + public function hasBlock($name, $context = array()) + { + return $this->template->hasBlock($name, $context); + } + + /** + * Returns defined block names in the template. + * + * @param array $context An array of parameters to pass to the template + * + * @return string[] An array of defined template block names + */ + public function getBlockNames($context = array()) + { + return $this->template->getBlockNames($context); + } + + /** + * Renders a template block. + * + * @param string $name The block name to render + * @param array $context An array of parameters to pass to the template + * + * @return string The rendered block + */ + public function renderBlock($name, $context = array()) + { + $context = $this->env->mergeGlobals($context); + $level = ob_get_level(); + ob_start(); + try { + $this->template->displayBlock($name, $context); + } catch (Exception $e) { + while (ob_get_level() > $level) { + ob_end_clean(); + } + + throw $e; + } catch (Throwable $e) { + while (ob_get_level() > $level) { + ob_end_clean(); + } + + throw $e; + } + + return ob_get_clean(); + } + + /** + * Displays a template block. + * + * @param string $name The block name to render + * @param array $context An array of parameters to pass to the template + */ + public function displayBlock($name, $context = array()) + { + $this->template->displayBlock($name, $this->env->mergeGlobals($context)); + } + + /** + * @return Twig_Source + */ + public function getSourceContext() + { + return $this->template->getSourceContext(); + } +} + +class_alias('Twig_TemplateWrapper', 'Twig\TemplateWrapper', false); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/Test.php b/lib/silex/vendor/twig/twig/lib/Twig/Test.php index 3c2d8590b..b450ec625 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/Test.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/Test.php @@ -3,7 +3,7 @@ /* * This file is part of Twig. * - * (c) 2012 Fabien Potencier + * (c) Fabien Potencier * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. diff --git a/lib/silex/vendor/twig/twig/lib/Twig/Test/Function.php b/lib/silex/vendor/twig/twig/lib/Twig/Test/Function.php index 5e76c711b..9e83c3f84 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/Test/Function.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/Test/Function.php @@ -3,7 +3,7 @@ /* * This file is part of Twig. * - * (c) 2010 Fabien Potencier + * (c) Fabien Potencier * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. diff --git a/lib/silex/vendor/twig/twig/lib/Twig/Test/IntegrationTestCase.php b/lib/silex/vendor/twig/twig/lib/Twig/Test/IntegrationTestCase.php index 96892a422..016951aa6 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/Test/IntegrationTestCase.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/Test/IntegrationTestCase.php @@ -3,23 +3,67 @@ /* * This file is part of Twig. * - * (c) 2010 Fabien Potencier + * (c) Fabien Potencier * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ +use PHPUnit\Framework\TestCase; + /** * Integration test helper. * * @author Fabien Potencier * @author Karma Dordrak */ -abstract class Twig_Test_IntegrationTestCase extends PHPUnit_Framework_TestCase +abstract class Twig_Test_IntegrationTestCase extends TestCase { - abstract protected function getExtensions(); + /** + * @return string + */ abstract protected function getFixturesDir(); + /** + * @return Twig_RuntimeLoaderInterface[] + */ + protected function getRuntimeLoaders() + { + return array(); + } + + /** + * @return Twig_ExtensionInterface[] + */ + protected function getExtensions() + { + return array(); + } + + /** + * @return Twig_SimpleFilter[] + */ + protected function getTwigFilters() + { + return array(); + } + + /** + * @return Twig_SimpleFunction[] + */ + protected function getTwigFunctions() + { + return array(); + } + + /** + * @return Twig_SimpleTest[] + */ + protected function getTwigTests() + { + return array(); + } + /** * @dataProvider getTests */ @@ -34,7 +78,7 @@ abstract class Twig_Test_IntegrationTestCase extends PHPUnit_Framework_TestCase */ public function testLegacyIntegration($file, $message, $condition, $templates, $exception, $outputs) { - $this->testIntegration($file, $message, $condition, $templates, $exception, $outputs); + $this->doIntegrationTest($file, $message, $condition, $templates, $exception, $outputs); } public function getTests($name, $legacyTests = false) @@ -56,13 +100,13 @@ abstract class Twig_Test_IntegrationTestCase extends PHPUnit_Framework_TestCase if (preg_match('/--TEST--\s*(.*?)\s*(?:--CONDITION--\s*(.*))?\s*((?:--TEMPLATE(?:\(.*?\))?--(?:.*?))+)\s*(?:--DATA--\s*(.*))?\s*--EXCEPTION--\s*(.*)/sx', $test, $match)) { $message = $match[1]; $condition = $match[2]; - $templates = $this->parseTemplates($match[3]); + $templates = self::parseTemplates($match[3]); $exception = $match[5]; $outputs = array(array(null, $match[4], null, '')); } elseif (preg_match('/--TEST--\s*(.*?)\s*(?:--CONDITION--\s*(.*))?\s*((?:--TEMPLATE(?:\(.*?\))?--(?:.*?))+)--DATA--.*?--EXPECT--.*/s', $test, $match)) { $message = $match[1]; $condition = $match[2]; - $templates = $this->parseTemplates($match[3]); + $templates = self::parseTemplates($match[3]); $exception = false; preg_match_all('/--DATA--(.*?)(?:--CONFIG--(.*?))?--EXPECT--(.*?)(?=\-\-DATA\-\-|$)/s', $test, $outputs, PREG_SET_ORDER); } else { @@ -72,7 +116,7 @@ abstract class Twig_Test_IntegrationTestCase extends PHPUnit_Framework_TestCase $tests[] = array(str_replace($fixturesDir.'/', '', $file), $message, $condition, $templates, $exception, $outputs); } - if (!$tests) { + if ($legacyTests && empty($tests)) { // add a dummy test to avoid a PHPUnit message return array(array('not', '-', '', array(), '', array())); } @@ -87,6 +131,10 @@ abstract class Twig_Test_IntegrationTestCase extends PHPUnit_Framework_TestCase protected function doIntegrationTest($file, $message, $condition, $templates, $exception, $outputs) { + if (!$outputs) { + $this->markTestSkipped('no legacy tests to run'); + } + if ($condition) { eval('$ret = '.$condition.';'); if (!$ret) { @@ -103,10 +151,26 @@ abstract class Twig_Test_IntegrationTestCase extends PHPUnit_Framework_TestCase ), $match[2] ? eval($match[2].';') : array()); $twig = new Twig_Environment($loader, $config); $twig->addGlobal('global', 'global'); + foreach ($this->getRuntimeLoaders() as $runtimeLoader) { + $twig->addRuntimeLoader($runtimeLoader); + } + foreach ($this->getExtensions() as $extension) { $twig->addExtension($extension); } + foreach ($this->getTwigFilters() as $filter) { + $twig->addFilter($filter); + } + + foreach ($this->getTwigTests() as $test) { + $twig->addTest($test); + } + + foreach ($this->getTwigFunctions() as $function) { + $twig->addFunction($function); + } + // avoid using the same PHP class name for different cases // only for PHP 5.2+ if (PHP_VERSION_ID >= 50300) { @@ -119,17 +183,14 @@ abstract class Twig_Test_IntegrationTestCase extends PHPUnit_Framework_TestCase $template = $twig->loadTemplate('index.twig'); } catch (Exception $e) { if (false !== $exception) { - $this->assertEquals(trim($exception), trim(sprintf('%s: %s', get_class($e), $e->getMessage()))); + $message = $e->getMessage(); + $this->assertSame(trim($exception), trim(sprintf('%s: %s', get_class($e), $message))); + $last = substr($message, strlen($message) - 1); + $this->assertTrue('.' === $last || '?' === $last, $message, 'Exception message must end with a dot or a question mark.'); return; } - if ($e instanceof Twig_Error_Syntax) { - $e->setTemplateFile($file); - - throw $e; - } - throw new Twig_Error(sprintf('%s: %s', get_class($e), $e->getMessage()), -1, $file, $e); } @@ -137,34 +198,36 @@ abstract class Twig_Test_IntegrationTestCase extends PHPUnit_Framework_TestCase $output = trim($template->render(eval($match[1].';')), "\n "); } catch (Exception $e) { if (false !== $exception) { - $this->assertEquals(trim($exception), trim(sprintf('%s: %s', get_class($e), $e->getMessage()))); + $this->assertSame(trim($exception), trim(sprintf('%s: %s', get_class($e), $e->getMessage()))); return; } - if ($e instanceof Twig_Error_Syntax) { - $e->setTemplateFile($file); - } else { - $e = new Twig_Error(sprintf('%s: %s', get_class($e), $e->getMessage()), -1, $file, $e); - } + $e = new Twig_Error(sprintf('%s: %s', get_class($e), $e->getMessage()), -1, $file, $e); $output = trim(sprintf('%s: %s', get_class($e), $e->getMessage())); } if (false !== $exception) { list($class) = explode(':', $exception); - $this->assertThat(null, new PHPUnit_Framework_Constraint_Exception($class)); + $constraintClass = class_exists('PHPUnit\Framework\Constraint\Exception') ? 'PHPUnit\Framework\Constraint\Exception' : 'PHPUnit_Framework_Constraint_Exception'; + $this->assertThat(null, new $constraintClass($class)); } $expected = trim($match[3], "\n "); - if ($expected != $output) { + if ($expected !== $output) { printf("Compiled templates that failed on case %d:\n", $i + 1); foreach (array_keys($templates) as $name) { echo "Template: $name\n"; - $source = $loader->getSource($name); - echo $twig->compile($twig->parse($twig->tokenize($source, $name))); + $loader = $twig->getLoader(); + if (!$loader instanceof Twig_SourceContextLoaderInterface) { + $source = new Twig_Source($loader->getSource($name), $name); + } else { + $source = $loader->getSourceContext($name); + } + echo $twig->compile($twig->parse($twig->tokenize($source))); } } $this->assertEquals($expected, $output, $message.' (in '.$file.')'); @@ -182,3 +245,5 @@ abstract class Twig_Test_IntegrationTestCase extends PHPUnit_Framework_TestCase return $templates; } } + +class_alias('Twig_Test_IntegrationTestCase', 'Twig\Test\IntegrationTestCase', false); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/Test/Method.php b/lib/silex/vendor/twig/twig/lib/Twig/Test/Method.php index 277998649..feccd5db3 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/Test/Method.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/Test/Method.php @@ -3,7 +3,7 @@ /* * This file is part of Twig. * - * (c) 2010 Fabien Potencier + * (c) Fabien Potencier * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -35,6 +35,6 @@ class Twig_Test_Method extends Twig_Test public function compile() { - return sprintf('$this->env->getExtension(\'%s\')->%s', $this->extension->getName(), $this->method); + return sprintf('$this->env->getExtension(\'%s\')->%s', get_class($this->extension), $this->method); } } diff --git a/lib/silex/vendor/twig/twig/lib/Twig/Test/Node.php b/lib/silex/vendor/twig/twig/lib/Twig/Test/Node.php index baef49cce..6098a5275 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/Test/Node.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/Test/Node.php @@ -3,7 +3,7 @@ /* * This file is part of Twig. * - * (c) 2010 Fabien Potencier + * (c) Fabien Potencier * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. diff --git a/lib/silex/vendor/twig/twig/lib/Twig/Test/NodeTestCase.php b/lib/silex/vendor/twig/twig/lib/Twig/Test/NodeTestCase.php index 908ef6196..479426757 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/Test/NodeTestCase.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/Test/NodeTestCase.php @@ -8,24 +8,31 @@ * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -abstract class Twig_Test_NodeTestCase extends PHPUnit_Framework_TestCase + +use PHPUnit\Framework\TestCase; + +abstract class Twig_Test_NodeTestCase extends TestCase { abstract public function getTests(); /** * @dataProvider getTests */ - public function testCompile($node, $source, $environment = null) + public function testCompile($node, $source, $environment = null, $isPattern = false) { - $this->assertNodeCompilation($source, $node, $environment); + $this->assertNodeCompilation($source, $node, $environment, $isPattern); } - public function assertNodeCompilation($source, Twig_Node $node, Twig_Environment $environment = null) + public function assertNodeCompilation($source, Twig_Node $node, Twig_Environment $environment = null, $isPattern = false) { $compiler = $this->getCompiler($environment); $compiler->compile($node); - $this->assertEquals($source, trim($compiler->getSource())); + if ($isPattern) { + $this->assertStringMatchesFormat($source, trim($compiler->getSource())); + } else { + $this->assertEquals($source, trim($compiler->getSource())); + } } protected function getCompiler(Twig_Environment $environment = null) @@ -42,6 +49,10 @@ abstract class Twig_Test_NodeTestCase extends PHPUnit_Framework_TestCase { $line = $line > 0 ? "// line {$line}\n" : ''; + if (PHP_VERSION_ID >= 70000) { + return sprintf('%s($context["%s"] ?? null)', $line, $name, $name); + } + if (PHP_VERSION_ID >= 50400) { return sprintf('%s(isset($context["%s"]) ? $context["%s"] : null)', $line, $name, $name); } @@ -58,3 +69,7 @@ abstract class Twig_Test_NodeTestCase extends PHPUnit_Framework_TestCase return '$this->getAttribute('; } } + +class_alias('Twig_Test_NodeTestCase', 'Twig\Test\NodeTestCase', false); +class_exists('Twig_Environment'); +class_exists('Twig_Node'); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/TestCallableInterface.php b/lib/silex/vendor/twig/twig/lib/Twig/TestCallableInterface.php index 98d345785..51ecb9a28 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/TestCallableInterface.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/TestCallableInterface.php @@ -3,7 +3,7 @@ /* * This file is part of Twig. * - * (c) 2012 Fabien Potencier + * (c) Fabien Potencier * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. diff --git a/lib/silex/vendor/twig/twig/lib/Twig/TestInterface.php b/lib/silex/vendor/twig/twig/lib/Twig/TestInterface.php index 2fa821ca3..91664075a 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/TestInterface.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/TestInterface.php @@ -3,7 +3,7 @@ /* * This file is part of Twig. * - * (c) 2010 Fabien Potencier + * (c) Fabien Potencier * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. diff --git a/lib/silex/vendor/twig/twig/lib/Twig/Token.php b/lib/silex/vendor/twig/twig/lib/Twig/Token.php index a0a029bcd..c7850eccc 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/Token.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/Token.php @@ -3,8 +3,8 @@ /* * This file is part of Twig. * - * (c) 2009 Fabien Potencier - * (c) 2009 Armin Ronacher + * (c) Fabien Potencier + * (c) Armin Ronacher * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,6 +14,8 @@ * Represents a Token. * * @author Fabien Potencier + * + * @final */ class Twig_Token { @@ -36,8 +38,6 @@ class Twig_Token const INTERPOLATION_END_TYPE = 11; /** - * Constructor. - * * @param int $type The type of the token * @param string $value The token value * @param int $lineno The line position in the source @@ -49,11 +49,6 @@ class Twig_Token $this->lineno = $lineno; } - /** - * Returns a string representation of the token. - * - * @return string A string representation of the token - */ public function __toString() { return sprintf('%s(%s)', self::typeToString($this->type, true), $this->value); @@ -63,9 +58,9 @@ class Twig_Token * Tests the current token for a type and/or a value. * * Parameters may be: - * * just type - * * type and value (or array of possible values) - * * just value (or array of possible values) (NAME_TYPE is used as type) + * * just type + * * type and value (or array of possible values) + * * just value (or array of possible values) (NAME_TYPE is used as type) * * @param array|int $type The type to test * @param array|string|null $values The token value @@ -87,9 +82,7 @@ class Twig_Token } /** - * Gets the line. - * - * @return int The source line + * @return int */ public function getLine() { @@ -97,9 +90,7 @@ class Twig_Token } /** - * Gets the token type. - * - * @return int The token type + * @return int */ public function getType() { @@ -107,9 +98,7 @@ class Twig_Token } /** - * Gets the token value. - * - * @return string The token value + * @return string */ public function getValue() { @@ -174,7 +163,7 @@ class Twig_Token } /** - * Returns the english representation of a given type. + * Returns the English representation of a given type. * * @param int $type The type as an integer * @@ -214,3 +203,5 @@ class Twig_Token } } } + +class_alias('Twig_Token', 'Twig\Token', false); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/TokenParser.php b/lib/silex/vendor/twig/twig/lib/Twig/TokenParser.php index fa9b6d867..1b4de14da 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/TokenParser.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/TokenParser.php @@ -3,7 +3,7 @@ /* * This file is part of Twig. * - * (c) 2009 Fabien Potencier + * (c) Fabien Potencier * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -23,11 +23,11 @@ abstract class Twig_TokenParser implements Twig_TokenParserInterface /** * Sets the parser associated with this token parser. - * - * @param Twig_Parser $parser A Twig_Parser instance */ public function setParser(Twig_Parser $parser) { $this->parser = $parser; } } + +class_alias('Twig_TokenParser', 'Twig\TokenParser\AbstractTokenParser', false); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/TokenParser/AutoEscape.php b/lib/silex/vendor/twig/twig/lib/Twig/TokenParser/AutoEscape.php index a8a3d7aa1..a20dedd18 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/TokenParser/AutoEscape.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/TokenParser/AutoEscape.php @@ -3,7 +3,7 @@ /* * This file is part of Twig. * - * (c) 2009 Fabien Potencier + * (c) Fabien Potencier * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -26,16 +26,11 @@ * using the js escaping strategy * {% endautoescape %} * + * + * @final */ class Twig_TokenParser_AutoEscape extends Twig_TokenParser { - /** - * Parses a token and returns a node. - * - * @param Twig_Token $token A Twig_Token instance - * - * @return Twig_NodeInterface A Twig_NodeInterface instance - */ public function parse(Twig_Token $token) { $lineno = $token->getLine(); @@ -46,7 +41,7 @@ class Twig_TokenParser_AutoEscape extends Twig_TokenParser } else { $expr = $this->parser->getExpressionParser()->parseExpression(); if (!$expr instanceof Twig_Node_Expression_Constant) { - throw new Twig_Error_Syntax('An escaping strategy must be a string or a Boolean.', $stream->getCurrent()->getLine(), $stream->getFilename()); + throw new Twig_Error_Syntax('An escaping strategy must be a string or a bool.', $stream->getCurrent()->getLine(), $stream->getSourceContext()); } $value = $expr->getAttribute('value'); @@ -57,10 +52,10 @@ class Twig_TokenParser_AutoEscape extends Twig_TokenParser } if ($compat && $stream->test(Twig_Token::NAME_TYPE)) { - @trigger_error('Using the autoescape tag with "true" or "false" before the strategy name is deprecated.', E_USER_DEPRECATED); + @trigger_error('Using the autoescape tag with "true" or "false" before the strategy name is deprecated since version 1.21.', E_USER_DEPRECATED); if (false === $value) { - throw new Twig_Error_Syntax('Unexpected escaping strategy as you set autoescaping to false.', $stream->getCurrent()->getLine(), $stream->getFilename()); + throw new Twig_Error_Syntax('Unexpected escaping strategy as you set autoescaping to false.', $stream->getCurrent()->getLine(), $stream->getSourceContext()); } $value = $stream->next()->getValue(); @@ -79,13 +74,10 @@ class Twig_TokenParser_AutoEscape extends Twig_TokenParser return $token->test('endautoescape'); } - /** - * Gets the tag name associated with this token parser. - * - * @return string The tag name - */ public function getTag() { return 'autoescape'; } } + +class_alias('Twig_TokenParser_AutoEscape', 'Twig\TokenParser\AutoEscapeTokenParser', false); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/TokenParser/Block.php b/lib/silex/vendor/twig/twig/lib/Twig/TokenParser/Block.php index 0a46200a1..f30f86b58 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/TokenParser/Block.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/TokenParser/Block.php @@ -3,8 +3,8 @@ /* * This file is part of Twig. * - * (c) 2009 Fabien Potencier - * (c) 2009 Armin Ronacher + * (c) Fabien Potencier + * (c) Armin Ronacher * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -19,23 +19,18 @@ * {% block title %}{% endblock %} - My Webpage * {% endblock %} * + * + * @final */ class Twig_TokenParser_Block extends Twig_TokenParser { - /** - * Parses a token and returns a node. - * - * @param Twig_Token $token A Twig_Token instance - * - * @return Twig_NodeInterface A Twig_NodeInterface instance - */ public function parse(Twig_Token $token) { $lineno = $token->getLine(); $stream = $this->parser->getStream(); $name = $stream->expect(Twig_Token::NAME_TYPE)->getValue(); if ($this->parser->hasBlock($name)) { - throw new Twig_Error_Syntax(sprintf("The block '$name' has already been defined line %d", $this->parser->getBlock($name)->getLine()), $stream->getCurrent()->getLine(), $stream->getFilename()); + throw new Twig_Error_Syntax(sprintf("The block '%s' has already been defined line %d.", $name, $this->parser->getBlock($name)->getTemplateLine()), $stream->getCurrent()->getLine(), $stream->getSourceContext()); } $this->parser->setBlock($name, $block = new Twig_Node_Block($name, new Twig_Node(array()), $lineno)); $this->parser->pushLocalScope(); @@ -47,7 +42,7 @@ class Twig_TokenParser_Block extends Twig_TokenParser $value = $token->getValue(); if ($value != $name) { - throw new Twig_Error_Syntax(sprintf('Expected endblock for block "%s" (but "%s" given)', $name, $value), $stream->getCurrent()->getLine(), $stream->getFilename()); + throw new Twig_Error_Syntax(sprintf('Expected endblock for block "%s" (but "%s" given).', $name, $value), $stream->getCurrent()->getLine(), $stream->getSourceContext()); } } } else { @@ -69,13 +64,10 @@ class Twig_TokenParser_Block extends Twig_TokenParser return $token->test('endblock'); } - /** - * Gets the tag name associated with this token parser. - * - * @return string The tag name - */ public function getTag() { return 'block'; } } + +class_alias('Twig_TokenParser_Block', 'Twig\TokenParser\BlockTokenParser', false); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/TokenParser/Do.php b/lib/silex/vendor/twig/twig/lib/Twig/TokenParser/Do.php index f50939dd2..8ce088089 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/TokenParser/Do.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/TokenParser/Do.php @@ -3,7 +3,7 @@ /* * This file is part of Twig. * - * (c) 2011 Fabien Potencier + * (c) Fabien Potencier * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -11,16 +11,11 @@ /** * Evaluates an expression, discarding the returned value. + * + * @final */ class Twig_TokenParser_Do extends Twig_TokenParser { - /** - * Parses a token and returns a node. - * - * @param Twig_Token $token A Twig_Token instance - * - * @return Twig_NodeInterface A Twig_NodeInterface instance - */ public function parse(Twig_Token $token) { $expr = $this->parser->getExpressionParser()->parseExpression(); @@ -30,13 +25,10 @@ class Twig_TokenParser_Do extends Twig_TokenParser return new Twig_Node_Do($expr, $token->getLine(), $this->getTag()); } - /** - * Gets the tag name associated with this token parser. - * - * @return string The tag name - */ public function getTag() { return 'do'; } } + +class_alias('Twig_TokenParser_Do', 'Twig\TokenParser\DoTokenParser', false); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/TokenParser/Embed.php b/lib/silex/vendor/twig/twig/lib/Twig/TokenParser/Embed.php index 69cb5f35f..44644cc6b 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/TokenParser/Embed.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/TokenParser/Embed.php @@ -3,7 +3,7 @@ /* * This file is part of Twig. * - * (c) 2012 Fabien Potencier + * (c) Fabien Potencier * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -11,16 +11,11 @@ /** * Embeds a template. + * + * @final */ class Twig_TokenParser_Embed extends Twig_TokenParser_Include { - /** - * Parses a token and returns a node. - * - * @param Twig_Token $token A Twig_Token instance - * - * @return Twig_NodeInterface A Twig_NodeInterface instance - */ public function parse(Twig_Token $token) { $stream = $this->parser->getStream(); @@ -29,24 +24,33 @@ class Twig_TokenParser_Embed extends Twig_TokenParser_Include list($variables, $only, $ignoreMissing) = $this->parseArguments(); + $parentToken = $fakeParentToken = new Twig_Token(Twig_Token::STRING_TYPE, '__parent__', $token->getLine()); + if ($parent instanceof Twig_Node_Expression_Constant) { + $parentToken = new Twig_Token(Twig_Token::STRING_TYPE, $parent->getAttribute('value'), $token->getLine()); + } elseif ($parent instanceof Twig_Node_Expression_Name) { + $parentToken = new Twig_Token(Twig_Token::NAME_TYPE, $parent->getAttribute('name'), $token->getLine()); + } + // inject a fake parent to make the parent() function work $stream->injectTokens(array( new Twig_Token(Twig_Token::BLOCK_START_TYPE, '', $token->getLine()), new Twig_Token(Twig_Token::NAME_TYPE, 'extends', $token->getLine()), - new Twig_Token(Twig_Token::STRING_TYPE, '__parent__', $token->getLine()), + $parentToken, new Twig_Token(Twig_Token::BLOCK_END_TYPE, '', $token->getLine()), )); $module = $this->parser->parse($stream, array($this, 'decideBlockEnd'), true); // override the parent with the correct one - $module->setNode('parent', $parent); + if ($fakeParentToken === $parentToken) { + $module->setNode('parent', $parent); + } $this->parser->embedTemplate($module); $stream->expect(Twig_Token::BLOCK_END_TYPE); - return new Twig_Node_Embed($module->getAttribute('filename'), $module->getAttribute('index'), $variables, $only, $ignoreMissing, $token->getLine(), $this->getTag()); + return new Twig_Node_Embed($module->getTemplateName(), $module->getAttribute('index'), $variables, $only, $ignoreMissing, $token->getLine(), $this->getTag()); } public function decideBlockEnd(Twig_Token $token) @@ -54,13 +58,10 @@ class Twig_TokenParser_Embed extends Twig_TokenParser_Include return $token->test('endembed'); } - /** - * Gets the tag name associated with this token parser. - * - * @return string The tag name - */ public function getTag() { return 'embed'; } } + +class_alias('Twig_TokenParser_Embed', 'Twig\TokenParser\EmbedTokenParser', false); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/TokenParser/Extends.php b/lib/silex/vendor/twig/twig/lib/Twig/TokenParser/Extends.php index f5ecee214..31168ccec 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/TokenParser/Extends.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/TokenParser/Extends.php @@ -3,8 +3,8 @@ /* * This file is part of Twig. * - * (c) 2009 Fabien Potencier - * (c) 2009 Armin Ronacher + * (c) Fabien Potencier + * (c) Armin Ronacher * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -16,37 +16,31 @@ *
  *  {% extends "base.html" %}
  * 
+ * + * @final */ class Twig_TokenParser_Extends extends Twig_TokenParser { - /** - * Parses a token and returns a node. - * - * @param Twig_Token $token A Twig_Token instance - * - * @return Twig_NodeInterface A Twig_NodeInterface instance - */ public function parse(Twig_Token $token) { + $stream = $this->parser->getStream(); + if (!$this->parser->isMainScope()) { - throw new Twig_Error_Syntax('Cannot extend from a block', $token->getLine(), $this->parser->getFilename()); + throw new Twig_Error_Syntax('Cannot extend from a block.', $token->getLine(), $stream->getSourceContext()); } if (null !== $this->parser->getParent()) { - throw new Twig_Error_Syntax('Multiple extends tags are forbidden', $token->getLine(), $this->parser->getFilename()); + throw new Twig_Error_Syntax('Multiple extends tags are forbidden.', $token->getLine(), $stream->getSourceContext()); } $this->parser->setParent($this->parser->getExpressionParser()->parseExpression()); - $this->parser->getStream()->expect(Twig_Token::BLOCK_END_TYPE); + $stream->expect(Twig_Token::BLOCK_END_TYPE); } - /** - * Gets the tag name associated with this token parser. - * - * @return string The tag name - */ public function getTag() { return 'extends'; } } + +class_alias('Twig_TokenParser_Extends', 'Twig\TokenParser\ExtendsTokenParser', false); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/TokenParser/Filter.php b/lib/silex/vendor/twig/twig/lib/Twig/TokenParser/Filter.php index 2b97475ae..760178293 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/TokenParser/Filter.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/TokenParser/Filter.php @@ -3,7 +3,7 @@ /* * This file is part of Twig. * - * (c) 2009 Fabien Potencier + * (c) Fabien Potencier * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -17,20 +17,15 @@ * This text becomes uppercase * {% endfilter %} * + * + * @final */ class Twig_TokenParser_Filter extends Twig_TokenParser { - /** - * Parses a token and returns a node. - * - * @param Twig_Token $token A Twig_Token instance - * - * @return Twig_NodeInterface A Twig_NodeInterface instance - */ public function parse(Twig_Token $token) { $name = $this->parser->getVarName(); - $ref = new Twig_Node_Expression_BlockReference(new Twig_Node_Expression_Constant($name, $token->getLine()), true, $token->getLine(), $this->getTag()); + $ref = new Twig_Node_Expression_BlockReference(new Twig_Node_Expression_Constant($name, $token->getLine()), null, $token->getLine(), $this->getTag()); $filter = $this->parser->getExpressionParser()->parseFilterExpressionRaw($ref, $this->getTag()); $this->parser->getStream()->expect(Twig_Token::BLOCK_END_TYPE); @@ -49,13 +44,10 @@ class Twig_TokenParser_Filter extends Twig_TokenParser return $token->test('endfilter'); } - /** - * Gets the tag name associated with this token parser. - * - * @return string The tag name - */ public function getTag() { return 'filter'; } } + +class_alias('Twig_TokenParser_Filter', 'Twig\TokenParser\FilterTokenParser', false); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/TokenParser/Flush.php b/lib/silex/vendor/twig/twig/lib/Twig/TokenParser/Flush.php index 4e15e7853..51832c77e 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/TokenParser/Flush.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/TokenParser/Flush.php @@ -3,7 +3,7 @@ /* * This file is part of Twig. * - * (c) 2011 Fabien Potencier + * (c) Fabien Potencier * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,16 +13,11 @@ * Flushes the output to the client. * * @see flush() + * + * @final */ class Twig_TokenParser_Flush extends Twig_TokenParser { - /** - * Parses a token and returns a node. - * - * @param Twig_Token $token A Twig_Token instance - * - * @return Twig_NodeInterface A Twig_NodeInterface instance - */ public function parse(Twig_Token $token) { $this->parser->getStream()->expect(Twig_Token::BLOCK_END_TYPE); @@ -30,13 +25,10 @@ class Twig_TokenParser_Flush extends Twig_TokenParser return new Twig_Node_Flush($token->getLine(), $this->getTag()); } - /** - * Gets the tag name associated with this token parser. - * - * @return string The tag name - */ public function getTag() { return 'flush'; } } + +class_alias('Twig_TokenParser_Flush', 'Twig\TokenParser\FlushTokenParser', false); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/TokenParser/For.php b/lib/silex/vendor/twig/twig/lib/Twig/TokenParser/For.php index 5c07d6395..63bf41d66 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/TokenParser/For.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/TokenParser/For.php @@ -3,8 +3,8 @@ /* * This file is part of Twig. * - * (c) 2009 Fabien Potencier - * (c) 2009 Armin Ronacher + * (c) Fabien Potencier + * (c) Armin Ronacher * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -20,16 +20,11 @@ * {% endfor %} * * + * + * @final */ class Twig_TokenParser_For extends Twig_TokenParser { - /** - * Parses a token and returns a node. - * - * @param Twig_Token $token A Twig_Token instance - * - * @return Twig_NodeInterface A Twig_NodeInterface instance - */ public function parse(Twig_Token $token) { $lineno = $token->getLine(); @@ -55,13 +50,13 @@ class Twig_TokenParser_For extends Twig_TokenParser if (count($targets) > 1) { $keyTarget = $targets->getNode(0); - $keyTarget = new Twig_Node_Expression_AssignName($keyTarget->getAttribute('name'), $keyTarget->getLine()); + $keyTarget = new Twig_Node_Expression_AssignName($keyTarget->getAttribute('name'), $keyTarget->getTemplateLine()); $valueTarget = $targets->getNode(1); - $valueTarget = new Twig_Node_Expression_AssignName($valueTarget->getAttribute('name'), $valueTarget->getLine()); + $valueTarget = new Twig_Node_Expression_AssignName($valueTarget->getAttribute('name'), $valueTarget->getTemplateLine()); } else { $keyTarget = new Twig_Node_Expression_AssignName('_key', $lineno); $valueTarget = $targets->getNode(0); - $valueTarget = new Twig_Node_Expression_AssignName($valueTarget->getAttribute('name'), $valueTarget->getLine()); + $valueTarget = new Twig_Node_Expression_AssignName($valueTarget->getAttribute('name'), $valueTarget->getTemplateLine()); } if ($ifexpr) { @@ -86,7 +81,7 @@ class Twig_TokenParser_For extends Twig_TokenParser protected function checkLoopUsageCondition(Twig_TokenStream $stream, Twig_NodeInterface $node) { if ($node instanceof Twig_Node_Expression_GetAttr && $node->getNode('node') instanceof Twig_Node_Expression_Name && 'loop' == $node->getNode('node')->getAttribute('name')) { - throw new Twig_Error_Syntax('The "loop" variable cannot be used in a looping condition', $node->getLine(), $stream->getFilename()); + throw new Twig_Error_Syntax('The "loop" variable cannot be used in a looping condition.', $node->getTemplateLine(), $stream->getSourceContext()); } foreach ($node as $n) { @@ -105,7 +100,7 @@ class Twig_TokenParser_For extends Twig_TokenParser if ($node instanceof Twig_Node_Expression_GetAttr && $node->getNode('node') instanceof Twig_Node_Expression_Name && 'loop' == $node->getNode('node')->getAttribute('name')) { $attribute = $node->getNode('attribute'); if ($attribute instanceof Twig_Node_Expression_Constant && in_array($attribute->getAttribute('value'), array('length', 'revindex0', 'revindex', 'last'))) { - throw new Twig_Error_Syntax(sprintf('The "loop.%s" variable is not defined when looping with a condition', $attribute->getAttribute('value')), $node->getLine(), $stream->getFilename()); + throw new Twig_Error_Syntax(sprintf('The "loop.%s" variable is not defined when looping with a condition.', $attribute->getAttribute('value')), $node->getTemplateLine(), $stream->getSourceContext()); } } @@ -123,13 +118,10 @@ class Twig_TokenParser_For extends Twig_TokenParser } } - /** - * Gets the tag name associated with this token parser. - * - * @return string The tag name - */ public function getTag() { return 'for'; } } + +class_alias('Twig_TokenParser_For', 'Twig\TokenParser\ForTokenParser', false); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/TokenParser/From.php b/lib/silex/vendor/twig/twig/lib/Twig/TokenParser/From.php index 5540efa4e..f3053da4b 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/TokenParser/From.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/TokenParser/From.php @@ -3,7 +3,7 @@ /* * This file is part of Twig. * - * (c) 2010 Fabien Potencier + * (c) Fabien Potencier * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -15,16 +15,11 @@ *
  *   {% from 'forms.html' import forms %}
  * 
+ * + * @final */ class Twig_TokenParser_From extends Twig_TokenParser { - /** - * Parses a token and returns a node. - * - * @param Twig_Token $token A Twig_Token instance - * - * @return Twig_NodeInterface A Twig_NodeInterface instance - */ public function parse(Twig_Token $token) { $macro = $this->parser->getExpressionParser()->parseExpression(); @@ -53,7 +48,7 @@ class Twig_TokenParser_From extends Twig_TokenParser foreach ($targets as $name => $alias) { if ($this->parser->isReservedMacroName($name)) { - throw new Twig_Error_Syntax(sprintf('"%s" cannot be an imported macro as it is a reserved keyword', $name), $token->getLine(), $stream->getFilename()); + throw new Twig_Error_Syntax(sprintf('"%s" cannot be an imported macro as it is a reserved keyword.', $name), $token->getLine(), $stream->getSourceContext()); } $this->parser->addImportedSymbol('function', $alias, 'get'.$name, $node->getNode('var')); @@ -62,13 +57,10 @@ class Twig_TokenParser_From extends Twig_TokenParser return $node; } - /** - * Gets the tag name associated with this token parser. - * - * @return string The tag name - */ public function getTag() { return 'from'; } } + +class_alias('Twig_TokenParser_From', 'Twig\TokenParser\FromTokenParser', false); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/TokenParser/If.php b/lib/silex/vendor/twig/twig/lib/Twig/TokenParser/If.php index 3d7d1f517..f081df3ab 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/TokenParser/If.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/TokenParser/If.php @@ -3,8 +3,8 @@ /* * This file is part of Twig. * - * (c) 2009 Fabien Potencier - * (c) 2009 Armin Ronacher + * (c) Fabien Potencier + * (c) Armin Ronacher * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -22,16 +22,11 @@ * * {% endif %} * + * + * @final */ class Twig_TokenParser_If extends Twig_TokenParser { - /** - * Parses a token and returns a node. - * - * @param Twig_Token $token A Twig_Token instance - * - * @return Twig_NodeInterface A Twig_NodeInterface instance - */ public function parse(Twig_Token $token) { $lineno = $token->getLine(); @@ -63,7 +58,7 @@ class Twig_TokenParser_If extends Twig_TokenParser break; default: - throw new Twig_Error_Syntax(sprintf('Unexpected end of template. Twig was looking for the following tags "else", "elseif", or "endif" to close the "if" block started at line %d)', $lineno), $stream->getCurrent()->getLine(), $stream->getFilename()); + throw new Twig_Error_Syntax(sprintf('Unexpected end of template. Twig was looking for the following tags "else", "elseif", or "endif" to close the "if" block started at line %d).', $lineno), $stream->getCurrent()->getLine(), $stream->getSourceContext()); } } @@ -82,13 +77,10 @@ class Twig_TokenParser_If extends Twig_TokenParser return $token->test(array('endif')); } - /** - * Gets the tag name associated with this token parser. - * - * @return string The tag name - */ public function getTag() { return 'if'; } } + +class_alias('Twig_TokenParser_If', 'Twig\TokenParser\IfTokenParser', false); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/TokenParser/Import.php b/lib/silex/vendor/twig/twig/lib/Twig/TokenParser/Import.php index e7050c70c..47802f502 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/TokenParser/Import.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/TokenParser/Import.php @@ -3,7 +3,7 @@ /* * This file is part of Twig. * - * (c) 2009 Fabien Potencier + * (c) Fabien Potencier * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -15,16 +15,11 @@ *
  *   {% import 'forms.html' as forms %}
  * 
+ * + * @final */ class Twig_TokenParser_Import extends Twig_TokenParser { - /** - * Parses a token and returns a node. - * - * @param Twig_Token $token A Twig_Token instance - * - * @return Twig_NodeInterface A Twig_NodeInterface instance - */ public function parse(Twig_Token $token) { $macro = $this->parser->getExpressionParser()->parseExpression(); @@ -37,13 +32,10 @@ class Twig_TokenParser_Import extends Twig_TokenParser return new Twig_Node_Import($macro, $var, $token->getLine(), $this->getTag()); } - /** - * Gets the tag name associated with this token parser. - * - * @return string The tag name - */ public function getTag() { return 'import'; } } + +class_alias('Twig_TokenParser_Import', 'Twig\TokenParser\ImportTokenParser', false); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/TokenParser/Include.php b/lib/silex/vendor/twig/twig/lib/Twig/TokenParser/Include.php index 9c3099a68..309f11db4 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/TokenParser/Include.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/TokenParser/Include.php @@ -3,8 +3,8 @@ /* * This file is part of Twig. * - * (c) 2009 Fabien Potencier - * (c) 2009 Armin Ronacher + * (c) Fabien Potencier + * (c) Armin Ronacher * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -21,13 +21,6 @@ */ class Twig_TokenParser_Include extends Twig_TokenParser { - /** - * Parses a token and returns a node. - * - * @param Twig_Token $token A Twig_Token instance - * - * @return Twig_NodeInterface A Twig_NodeInterface instance - */ public function parse(Twig_Token $token) { $expr = $this->parser->getExpressionParser()->parseExpression(); @@ -63,13 +56,10 @@ class Twig_TokenParser_Include extends Twig_TokenParser return array($variables, $only, $ignoreMissing); } - /** - * Gets the tag name associated with this token parser. - * - * @return string The tag name - */ public function getTag() { return 'include'; } } + +class_alias('Twig_TokenParser_Include', 'Twig\TokenParser\IncludeTokenParser', false); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/TokenParser/Macro.php b/lib/silex/vendor/twig/twig/lib/Twig/TokenParser/Macro.php index ad910b537..4287934ba 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/TokenParser/Macro.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/TokenParser/Macro.php @@ -3,7 +3,7 @@ /* * This file is part of Twig. * - * (c) 2009 Fabien Potencier + * (c) Fabien Potencier * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -17,16 +17,11 @@ * * {% endmacro %} * + * + * @final */ class Twig_TokenParser_Macro extends Twig_TokenParser { - /** - * Parses a token and returns a node. - * - * @param Twig_Token $token A Twig_Token instance - * - * @return Twig_NodeInterface A Twig_NodeInterface instance - */ public function parse(Twig_Token $token) { $lineno = $token->getLine(); @@ -42,7 +37,7 @@ class Twig_TokenParser_Macro extends Twig_TokenParser $value = $token->getValue(); if ($value != $name) { - throw new Twig_Error_Syntax(sprintf('Expected endmacro for macro "%s" (but "%s" given)', $name, $value), $stream->getCurrent()->getLine(), $stream->getFilename()); + throw new Twig_Error_Syntax(sprintf('Expected endmacro for macro "%s" (but "%s" given).', $name, $value), $stream->getCurrent()->getLine(), $stream->getSourceContext()); } } $this->parser->popLocalScope(); @@ -56,13 +51,10 @@ class Twig_TokenParser_Macro extends Twig_TokenParser return $token->test('endmacro'); } - /** - * Gets the tag name associated with this token parser. - * - * @return string The tag name - */ public function getTag() { return 'macro'; } } + +class_alias('Twig_TokenParser_Macro', 'Twig\TokenParser\MacroTokenParser', false); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/TokenParser/Sandbox.php b/lib/silex/vendor/twig/twig/lib/Twig/TokenParser/Sandbox.php index 9457325ab..b8f581cbc 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/TokenParser/Sandbox.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/TokenParser/Sandbox.php @@ -3,7 +3,7 @@ /* * This file is part of Twig. * - * (c) 2010 Fabien Potencier + * (c) Fabien Potencier * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -19,21 +19,17 @@ * * * @see http://www.twig-project.org/doc/api.html#sandbox-extension for details + * + * @final */ class Twig_TokenParser_Sandbox extends Twig_TokenParser { - /** - * Parses a token and returns a node. - * - * @param Twig_Token $token A Twig_Token instance - * - * @return Twig_NodeInterface A Twig_NodeInterface instance - */ public function parse(Twig_Token $token) { - $this->parser->getStream()->expect(Twig_Token::BLOCK_END_TYPE); + $stream = $this->parser->getStream(); + $stream->expect(Twig_Token::BLOCK_END_TYPE); $body = $this->parser->subparse(array($this, 'decideBlockEnd'), true); - $this->parser->getStream()->expect(Twig_Token::BLOCK_END_TYPE); + $stream->expect(Twig_Token::BLOCK_END_TYPE); // in a sandbox tag, only include tags are allowed if (!$body instanceof Twig_Node_Include) { @@ -43,7 +39,7 @@ class Twig_TokenParser_Sandbox extends Twig_TokenParser } if (!$node instanceof Twig_Node_Include) { - throw new Twig_Error_Syntax('Only "include" tags are allowed within a "sandbox" section', $node->getLine(), $this->parser->getFilename()); + throw new Twig_Error_Syntax('Only "include" tags are allowed within a "sandbox" section.', $node->getTemplateLine(), $stream->getSourceContext()); } } } @@ -56,13 +52,10 @@ class Twig_TokenParser_Sandbox extends Twig_TokenParser return $token->test('endsandbox'); } - /** - * Gets the tag name associated with this token parser. - * - * @return string The tag name - */ public function getTag() { return 'sandbox'; } } + +class_alias('Twig_TokenParser_Sandbox', 'Twig\TokenParser\SandboxTokenParser', false); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/TokenParser/Set.php b/lib/silex/vendor/twig/twig/lib/Twig/TokenParser/Set.php index 0b419095f..48c6b3aeb 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/TokenParser/Set.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/TokenParser/Set.php @@ -3,7 +3,7 @@ /* * This file is part of Twig. * - * (c) 2009 Fabien Potencier + * (c) Fabien Potencier * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -25,16 +25,11 @@ * * {% set foo %}Some content{% endset %} * + * + * @final */ class Twig_TokenParser_Set extends Twig_TokenParser { - /** - * Parses a token and returns a node. - * - * @param Twig_Token $token A Twig_Token instance - * - * @return Twig_NodeInterface A Twig_NodeInterface instance - */ public function parse(Twig_Token $token) { $lineno = $token->getLine(); @@ -48,13 +43,13 @@ class Twig_TokenParser_Set extends Twig_TokenParser $stream->expect(Twig_Token::BLOCK_END_TYPE); if (count($names) !== count($values)) { - throw new Twig_Error_Syntax('When using set, you must have the same number of variables and assignments.', $stream->getCurrent()->getLine(), $stream->getFilename()); + throw new Twig_Error_Syntax('When using set, you must have the same number of variables and assignments.', $stream->getCurrent()->getLine(), $stream->getSourceContext()); } } else { $capture = true; if (count($names) > 1) { - throw new Twig_Error_Syntax('When using set with a block, you cannot have a multi-target.', $stream->getCurrent()->getLine(), $stream->getFilename()); + throw new Twig_Error_Syntax('When using set with a block, you cannot have a multi-target.', $stream->getCurrent()->getLine(), $stream->getSourceContext()); } $stream->expect(Twig_Token::BLOCK_END_TYPE); @@ -71,13 +66,10 @@ class Twig_TokenParser_Set extends Twig_TokenParser return $token->test('endset'); } - /** - * Gets the tag name associated with this token parser. - * - * @return string The tag name - */ public function getTag() { return 'set'; } } + +class_alias('Twig_TokenParser_Set', 'Twig\TokenParser\SetTokenParser', false); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/TokenParser/Spaceless.php b/lib/silex/vendor/twig/twig/lib/Twig/TokenParser/Spaceless.php index 1e3fa8f3e..cecf27c67 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/TokenParser/Spaceless.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/TokenParser/Spaceless.php @@ -3,7 +3,7 @@ /* * This file is part of Twig. * - * (c) 2010 Fabien Potencier + * (c) Fabien Potencier * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -21,16 +21,11 @@ * * {# output will be
foo
#} * + * + * @final */ class Twig_TokenParser_Spaceless extends Twig_TokenParser { - /** - * Parses a token and returns a node. - * - * @param Twig_Token $token A Twig_Token instance - * - * @return Twig_NodeInterface A Twig_NodeInterface instance - */ public function parse(Twig_Token $token) { $lineno = $token->getLine(); @@ -47,13 +42,10 @@ class Twig_TokenParser_Spaceless extends Twig_TokenParser return $token->test('endspaceless'); } - /** - * Gets the tag name associated with this token parser. - * - * @return string The tag name - */ public function getTag() { return 'spaceless'; } } + +class_alias('Twig_TokenParser_Spaceless', 'Twig\TokenParser\SpacelessTokenParser', false); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/TokenParser/Use.php b/lib/silex/vendor/twig/twig/lib/Twig/TokenParser/Use.php index 3ea68b1a5..f15a91ea0 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/TokenParser/Use.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/TokenParser/Use.php @@ -3,7 +3,7 @@ /* * This file is part of Twig. * - * (c) 2011 Fabien Potencier + * (c) Fabien Potencier * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -22,23 +22,18 @@ * * * @see http://www.twig-project.org/doc/templates.html#horizontal-reuse for details. + * + * @final */ class Twig_TokenParser_Use extends Twig_TokenParser { - /** - * Parses a token and returns a node. - * - * @param Twig_Token $token A Twig_Token instance - * - * @return Twig_NodeInterface A Twig_NodeInterface instance - */ public function parse(Twig_Token $token) { $template = $this->parser->getExpressionParser()->parseExpression(); $stream = $this->parser->getStream(); if (!$template instanceof Twig_Node_Expression_Constant) { - throw new Twig_Error_Syntax('The template references in a "use" statement must be a string.', $stream->getCurrent()->getLine(), $stream->getFilename()); + throw new Twig_Error_Syntax('The template references in a "use" statement must be a string.', $stream->getCurrent()->getLine(), $stream->getSourceContext()); } $targets = array(); @@ -62,15 +57,14 @@ class Twig_TokenParser_Use extends Twig_TokenParser $stream->expect(Twig_Token::BLOCK_END_TYPE); $this->parser->addTrait(new Twig_Node(array('template' => $template, 'targets' => new Twig_Node($targets)))); + + return new Twig_Node(); } - /** - * Gets the tag name associated with this token parser. - * - * @return string The tag name - */ public function getTag() { return 'use'; } } + +class_alias('Twig_TokenParser_Use', 'Twig\TokenParser\UseTokenParser', false); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/TokenParser/With.php b/lib/silex/vendor/twig/twig/lib/Twig/TokenParser/With.php new file mode 100644 index 000000000..7a692597c --- /dev/null +++ b/lib/silex/vendor/twig/twig/lib/Twig/TokenParser/With.php @@ -0,0 +1,52 @@ + + * + * @final + */ +class Twig_TokenParser_With extends Twig_TokenParser +{ + public function parse(Twig_Token $token) + { + $stream = $this->parser->getStream(); + + $variables = null; + $only = false; + if (!$stream->test(Twig_Token::BLOCK_END_TYPE)) { + $variables = $this->parser->getExpressionParser()->parseExpression(); + $only = $stream->nextIf(Twig_Token::NAME_TYPE, 'only'); + } + + $stream->expect(Twig_Token::BLOCK_END_TYPE); + + $body = $this->parser->subparse(array($this, 'decideWithEnd'), true); + + $stream->expect(Twig_Token::BLOCK_END_TYPE); + + return new Twig_Node_With($body, $variables, $only, $token->getLine(), $this->getTag()); + } + + public function decideWithEnd(Twig_Token $token) + { + return $token->test('endwith'); + } + + public function getTag() + { + return 'with'; + } +} + +class_alias('Twig_TokenParser_With', 'Twig\TokenParser\WithTokenParser', false); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/TokenParserBroker.php b/lib/silex/vendor/twig/twig/lib/Twig/TokenParserBroker.php index 6ca73fb38..a64013508 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/TokenParserBroker.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/TokenParserBroker.php @@ -3,8 +3,8 @@ /* * This file is part of Twig. * - * (c) 2010 Fabien Potencier - * (c) 2010 Arnaud Le Blanc + * (c) Fabien Potencier + * (c) Arnaud Le Blanc * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -24,10 +24,9 @@ class Twig_TokenParserBroker implements Twig_TokenParserBrokerInterface protected $brokers = array(); /** - * Constructor. - * - * @param array|Traversable $parsers A Traversable of Twig_TokenParserInterface instances - * @param array|Traversable $brokers A Traversable of Twig_TokenParserBrokerInterface instances + * @param array|Traversable $parsers A Traversable of Twig_TokenParserInterface instances + * @param array|Traversable $brokers A Traversable of Twig_TokenParserBrokerInterface instances + * @param bool $triggerDeprecationError */ public function __construct($parsers = array(), $brokers = array(), $triggerDeprecationError = true) { @@ -37,33 +36,23 @@ class Twig_TokenParserBroker implements Twig_TokenParserBrokerInterface foreach ($parsers as $parser) { if (!$parser instanceof Twig_TokenParserInterface) { - throw new LogicException('$parsers must a an array of Twig_TokenParserInterface'); + throw new LogicException('$parsers must a an array of Twig_TokenParserInterface.'); } $this->parsers[$parser->getTag()] = $parser; } foreach ($brokers as $broker) { if (!$broker instanceof Twig_TokenParserBrokerInterface) { - throw new LogicException('$brokers must a an array of Twig_TokenParserBrokerInterface'); + throw new LogicException('$brokers must a an array of Twig_TokenParserBrokerInterface.'); } $this->brokers[] = $broker; } } - /** - * Adds a TokenParser. - * - * @param Twig_TokenParserInterface $parser A Twig_TokenParserInterface instance - */ public function addTokenParser(Twig_TokenParserInterface $parser) { $this->parsers[$parser->getTag()] = $parser; } - /** - * Removes a TokenParser. - * - * @param Twig_TokenParserInterface $parser A Twig_TokenParserInterface instance - */ public function removeTokenParser(Twig_TokenParserInterface $parser) { $name = $parser->getTag(); @@ -72,21 +61,11 @@ class Twig_TokenParserBroker implements Twig_TokenParserBrokerInterface } } - /** - * Adds a TokenParserBroker. - * - * @param Twig_TokenParserBroker $broker A Twig_TokenParserBroker instance - */ public function addTokenParserBroker(Twig_TokenParserBroker $broker) { $this->brokers[] = $broker; } - /** - * Removes a TokenParserBroker. - * - * @param Twig_TokenParserBroker $broker A Twig_TokenParserBroker instance - */ public function removeTokenParserBroker(Twig_TokenParserBroker $broker) { if (false !== $pos = array_search($broker, $this->brokers)) { diff --git a/lib/silex/vendor/twig/twig/lib/Twig/TokenParserBrokerInterface.php b/lib/silex/vendor/twig/twig/lib/Twig/TokenParserBrokerInterface.php index 3ec2a8801..6c93f5ea1 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/TokenParserBrokerInterface.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/TokenParserBrokerInterface.php @@ -3,8 +3,8 @@ /* * This file is part of Twig. * - * (c) 2010 Fabien Potencier - * (c) 2010 Arnaud Le Blanc + * (c) Fabien Potencier + * (c) Arnaud Le Blanc * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -26,14 +26,12 @@ interface Twig_TokenParserBrokerInterface * * @param string $tag A tag name * - * @return null|Twig_TokenParserInterface A Twig_TokenParserInterface or null if no suitable TokenParser was found + * @return Twig_TokenParserInterface|null A Twig_TokenParserInterface or null if no suitable TokenParser was found */ public function getTokenParser($tag); /** * Calls Twig_TokenParserInterface::setParser on all parsers the implementation knows of. - * - * @param Twig_ParserInterface $parser A Twig_ParserInterface interface */ public function setParser(Twig_ParserInterface $parser); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/TokenParserInterface.php b/lib/silex/vendor/twig/twig/lib/Twig/TokenParserInterface.php index 12ec39617..14acc8084 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/TokenParserInterface.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/TokenParserInterface.php @@ -3,7 +3,7 @@ /* * This file is part of Twig. * - * (c) 2010 Fabien Potencier + * (c) Fabien Potencier * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -18,17 +18,13 @@ interface Twig_TokenParserInterface { /** * Sets the parser associated with this token parser. - * - * @param Twig_Parser $parser A Twig_Parser instance */ public function setParser(Twig_Parser $parser); /** * Parses a token and returns a node. * - * @param Twig_Token $token A Twig_Token instance - * - * @return Twig_NodeInterface A Twig_NodeInterface instance + * @return Twig_NodeInterface * * @throws Twig_Error_Syntax */ @@ -41,3 +37,7 @@ interface Twig_TokenParserInterface */ public function getTag(); } + +class_alias('Twig_TokenParserInterface', 'Twig\TokenParser\TokenParserInterface', false); +class_exists('Twig_Parser'); +class_exists('Twig_Token'); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/TokenStream.php b/lib/silex/vendor/twig/twig/lib/Twig/TokenStream.php index 8d2e220fe..e1bd7ca1e 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/TokenStream.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/TokenStream.php @@ -3,8 +3,8 @@ /* * This file is part of Twig. * - * (c) 2009 Fabien Potencier - * (c) 2009 Armin Ronacher + * (c) Fabien Potencier + * (c) Armin Ronacher * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,6 +13,8 @@ /** * Represents a token stream. * + * @final + * * @author Fabien Potencier */ class Twig_TokenStream @@ -21,23 +23,30 @@ class Twig_TokenStream protected $current = 0; protected $filename; - /** - * Constructor. - * - * @param array $tokens An array of tokens - * @param string $filename The name of the filename which tokens are associated with - */ - public function __construct(array $tokens, $filename = null) - { - $this->tokens = $tokens; - $this->filename = $filename; - } + private $source; /** - * Returns a string representation of the token stream. - * - * @return string + * @param array $tokens An array of tokens + * @param string|null $name The name of the template which tokens are associated with + * @param string|null $source The source code associated with the tokens */ + public function __construct(array $tokens, $name = null, $source = null) + { + if (!$name instanceof Twig_Source) { + if (null !== $name || null !== $source) { + @trigger_error(sprintf('Passing a string as the $name argument of %s() is deprecated since version 1.27. Pass a Twig_Source instance instead.', __METHOD__), E_USER_DEPRECATED); + } + $this->source = new Twig_Source($source, $name); + } else { + $this->source = $name; + } + + $this->tokens = $tokens; + + // deprecated, not used anymore, to be removed in 2.0 + $this->filename = $this->source->getName(); + } + public function __toString() { return implode("\n", $this->tokens); @@ -56,7 +65,7 @@ class Twig_TokenStream public function next() { if (!isset($this->tokens[++$this->current])) { - throw new Twig_Error_Syntax('Unexpected end of template', $this->tokens[$this->current - 1]->getLine(), $this->filename); + throw new Twig_Error_Syntax('Unexpected end of template.', $this->tokens[$this->current - 1]->getLine(), $this->source); } return $this->tokens[$this->current - 1]; @@ -84,12 +93,12 @@ class Twig_TokenStream $token = $this->tokens[$this->current]; if (!$token->test($type, $value)) { $line = $token->getLine(); - throw new Twig_Error_Syntax(sprintf('%sUnexpected token "%s" of value "%s" ("%s" expected%s)', + throw new Twig_Error_Syntax(sprintf('%sUnexpected token "%s" of value "%s" ("%s" expected%s).', $message ? $message.'. ' : '', Twig_Token::typeToEnglish($token->getType()), $token->getValue(), Twig_Token::typeToEnglish($type), $value ? sprintf(' with value "%s"', $value) : ''), $line, - $this->filename + $this->source ); } $this->next(); @@ -107,7 +116,7 @@ class Twig_TokenStream public function look($number = 1) { if (!isset($this->tokens[$this->current + $number])) { - throw new Twig_Error_Syntax('Unexpected end of template', $this->tokens[$this->current + $number - 1]->getLine(), $this->filename); + throw new Twig_Error_Syntax('Unexpected end of template.', $this->tokens[$this->current + $number - 1]->getLine(), $this->source); } return $this->tokens[$this->current + $number]; @@ -134,8 +143,6 @@ class Twig_TokenStream } /** - * Gets the current token. - * * @return Twig_Token */ public function getCurrent() @@ -144,12 +151,46 @@ class Twig_TokenStream } /** - * Gets the filename associated with this stream. + * Gets the name associated with this stream (null if not defined). * - * @return string + * @return string|null + * + * @deprecated since 1.27 (to be removed in 2.0) */ public function getFilename() { - return $this->filename; + @trigger_error(sprintf('The %s() method is deprecated since version 1.27 and will be removed in 2.0. Use getSourceContext() instead.', __METHOD__), E_USER_DEPRECATED); + + return $this->source->getName(); + } + + /** + * Gets the source code associated with this stream. + * + * @return string + * + * @internal Don't use this as it might be empty depending on the environment configuration + * + * @deprecated since 1.27 (to be removed in 2.0) + */ + public function getSource() + { + @trigger_error(sprintf('The %s() method is deprecated since version 1.27 and will be removed in 2.0. Use getSourceContext() instead.', __METHOD__), E_USER_DEPRECATED); + + return $this->source->getCode(); + } + + /** + * Gets the source associated with this stream. + * + * @return Twig_Source + * + * @internal + */ + public function getSourceContext() + { + return $this->source; } } + +class_alias('Twig_TokenStream', 'Twig\TokenStream', false); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/Util/DeprecationCollector.php b/lib/silex/vendor/twig/twig/lib/Twig/Util/DeprecationCollector.php index e406f0aa7..c7bf53be2 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/Util/DeprecationCollector.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/Util/DeprecationCollector.php @@ -11,6 +11,8 @@ /** * @author Fabien Potencier + * + * @final */ class Twig_Util_DeprecationCollector { @@ -28,7 +30,7 @@ class Twig_Util_DeprecationCollector * @param string $dir A directory where templates are stored * @param string $ext Limit the loaded templates by extension * - * @return array() An array of deprecations + * @return array An array of deprecations */ public function collectDir($dir, $ext = '.twig') { @@ -44,11 +46,11 @@ class Twig_Util_DeprecationCollector /** * Returns deprecations for passed templates. * - * @param Iterator $iterator An iterator of templates (where keys are template names and values the contents of the template) + * @param Traversable $iterator An iterator of templates (where keys are template names and values the contents of the template) * - * @return array() An array of deprecations + * @return array An array of deprecations */ - public function collect(Iterator $iterator) + public function collect(Traversable $iterator) { $this->deprecations = array(); @@ -56,7 +58,7 @@ class Twig_Util_DeprecationCollector foreach ($iterator as $name => $contents) { try { - $this->twig->parse($this->twig->tokenize($contents, $name)); + $this->twig->parse($this->twig->tokenize(new Twig_Source($contents, $name))); } catch (Twig_Error_Syntax $e) { // ignore templates containing syntax errors } @@ -80,3 +82,5 @@ class Twig_Util_DeprecationCollector } } } + +class_alias('Twig_Util_DeprecationCollector', 'Twig\Util\DeprecationCollector', false); diff --git a/lib/silex/vendor/twig/twig/lib/Twig/Util/TemplateDirIterator.php b/lib/silex/vendor/twig/twig/lib/Twig/Util/TemplateDirIterator.php index 3fb893278..c8682335d 100644 --- a/lib/silex/vendor/twig/twig/lib/Twig/Util/TemplateDirIterator.php +++ b/lib/silex/vendor/twig/twig/lib/Twig/Util/TemplateDirIterator.php @@ -24,3 +24,5 @@ class Twig_Util_TemplateDirIterator extends IteratorIterator return (string) parent::key(); } } + +class_alias('Twig_Util_TemplateDirIterator', 'Twig\Util\TemplateDirIterator', false); diff --git a/lib/silex/vendor/twig/twig/phpunit.xml.dist b/lib/silex/vendor/twig/twig/phpunit.xml.dist index 6f6d1d25a..ce7732785 100644 --- a/lib/silex/vendor/twig/twig/phpunit.xml.dist +++ b/lib/silex/vendor/twig/twig/phpunit.xml.dist @@ -17,6 +17,14 @@ + + + + + + + + ./lib/Twig/ diff --git a/lib/silex/vendor/twig/twig/src/Cache/CacheInterface.php b/lib/silex/vendor/twig/twig/src/Cache/CacheInterface.php new file mode 100644 index 000000000..2e35e3ba0 --- /dev/null +++ b/lib/silex/vendor/twig/twig/src/Cache/CacheInterface.php @@ -0,0 +1,11 @@ + + */ +interface RuntimeExtensionInterface +{ +} diff --git a/lib/silex/vendor/twig/twig/src/Extension/SandboxExtension.php b/lib/silex/vendor/twig/twig/src/Extension/SandboxExtension.php new file mode 100644 index 000000000..0c244ffc6 --- /dev/null +++ b/lib/silex/vendor/twig/twig/src/Extension/SandboxExtension.php @@ -0,0 +1,11 @@ +classname = '__Twig_Tests_Cache_FilesystemTest_Template_'.$nonce; + $this->directory = sys_get_temp_dir().'/twig-test'; + $this->cache = new Twig_Cache_Filesystem($this->directory); + } + + protected function tearDown() + { + if (file_exists($this->directory)) { + Twig_Tests_FilesystemHelper::removeDir($this->directory); + } + } + + public function testLoad() + { + $key = $this->directory.'/cache/cachefile.php'; + + $dir = dirname($key); + @mkdir($dir, 0777, true); + $this->assertTrue(is_dir($dir)); + $this->assertFalse(class_exists($this->classname, false)); + + $content = $this->generateSource(); + file_put_contents($key, $content); + + $this->cache->load($key); + + $this->assertTrue(class_exists($this->classname, false)); + } + + public function testLoadMissing() + { + $key = $this->directory.'/cache/cachefile.php'; + + $this->assertFalse(class_exists($this->classname, false)); + + $this->cache->load($key); + + $this->assertFalse(class_exists($this->classname, false)); + } + + public function testWrite() + { + $key = $this->directory.'/cache/cachefile.php'; + $content = $this->generateSource(); + + $this->assertFileNotExists($key); + $this->assertFileNotExists($this->directory); + + $this->cache->write($key, $content); + + $this->assertFileExists($this->directory); + $this->assertFileExists($key); + $this->assertSame(file_get_contents($key), $content); + } + + /** + * @expectedException RuntimeException + * @expectedExceptionMessage Unable to create the cache directory + */ + public function testWriteFailMkdir() + { + if (defined('PHP_WINDOWS_VERSION_BUILD')) { + $this->markTestSkipped('Read-only directories not possible on Windows.'); + } + + $key = $this->directory.'/cache/cachefile.php'; + $content = $this->generateSource(); + + $this->assertFileNotExists($key); + + // Create read-only root directory. + @mkdir($this->directory, 0555, true); + $this->assertTrue(is_dir($this->directory)); + + $this->cache->write($key, $content); + } + + /** + * @expectedException RuntimeException + * @expectedExceptionMessage Unable to write in the cache directory + */ + public function testWriteFailDirWritable() + { + if (defined('PHP_WINDOWS_VERSION_BUILD')) { + $this->markTestSkipped('Read-only directories not possible on Windows.'); + } + + $key = $this->directory.'/cache/cachefile.php'; + $content = $this->generateSource(); + + $this->assertFileNotExists($key); + + // Create root directory. + @mkdir($this->directory, 0777, true); + // Create read-only subdirectory. + @mkdir($this->directory.'/cache', 0555); + $this->assertTrue(is_dir($this->directory.'/cache')); + + $this->cache->write($key, $content); + } + + /** + * @expectedException RuntimeException + * @expectedExceptionMessage Failed to write cache file + */ + public function testWriteFailWriteFile() + { + $key = $this->directory.'/cache/cachefile.php'; + $content = $this->generateSource(); + + $this->assertFileNotExists($key); + + // Create a directory in the place of the cache file. + @mkdir($key, 0777, true); + $this->assertTrue(is_dir($key)); + + $this->cache->write($key, $content); + } + + public function testGetTimestamp() + { + $key = $this->directory.'/cache/cachefile.php'; + + $dir = dirname($key); + @mkdir($dir, 0777, true); + $this->assertTrue(is_dir($dir)); + + // Create the file with a specific modification time. + touch($key, 1234567890); + + $this->assertSame(1234567890, $this->cache->getTimestamp($key)); + } + + public function testGetTimestampMissingFile() + { + $key = $this->directory.'/cache/cachefile.php'; + $this->assertSame(0, $this->cache->getTimestamp($key)); + } + + /** + * Test file cache is tolerant towards trailing (back)slashes on the configured cache directory. + * + * @dataProvider provideDirectories + */ + public function testGenerateKey($expected, $input) + { + $cache = new Twig_Cache_Filesystem($input); + $this->assertRegExp($expected, $cache->generateKey('_test_', get_class($this))); + } + + public function provideDirectories() + { + $pattern = '#a/b/[a-zA-Z0-9]+/[a-zA-Z0-9]+.php$#'; + + return array( + array($pattern, 'a/b'), + array($pattern, 'a/b/'), + array($pattern, 'a/b\\'), + array($pattern, 'a/b\\/'), + array($pattern, 'a/b\\//'), + array('#/'.substr($pattern, 1), '/a/b'), + ); + } + + private function generateSource() + { + return strtr(' $this->classname, + )); + } +} diff --git a/lib/silex/vendor/twig/twig/test/Twig/Tests/CompilerTest.php b/lib/silex/vendor/twig/twig/test/Twig/Tests/CompilerTest.php index bc25f117b..4d4b8df9e 100644 --- a/lib/silex/vendor/twig/twig/test/Twig/Tests/CompilerTest.php +++ b/lib/silex/vendor/twig/twig/test/Twig/Tests/CompilerTest.php @@ -9,11 +9,11 @@ * file that was distributed with this source code. */ -class Twig_Tests_CompilerTest extends PHPUnit_Framework_TestCase +class Twig_Tests_CompilerTest extends \PHPUnit\Framework\TestCase { public function testReprNumericValueWithLocale() { - $compiler = new Twig_Compiler(new Twig_Environment($this->getMock('Twig_LoaderInterface'))); + $compiler = new Twig_Compiler(new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock())); $locale = setlocale(LC_NUMERIC, 0); if (false === $locale) { diff --git a/lib/silex/vendor/twig/twig/test/Twig/Tests/ContainerRuntimeLoaderTest.php b/lib/silex/vendor/twig/twig/test/Twig/Tests/ContainerRuntimeLoaderTest.php new file mode 100644 index 000000000..c111f9284 --- /dev/null +++ b/lib/silex/vendor/twig/twig/test/Twig/Tests/ContainerRuntimeLoaderTest.php @@ -0,0 +1,40 @@ +getMockBuilder('Psr\Container\ContainerInterface')->getMock(); + $container->expects($this->once())->method('has')->with('stdClass')->willReturn(true); + $container->expects($this->once())->method('get')->with('stdClass')->willReturn(new stdClass()); + + $loader = new Twig_ContainerRuntimeLoader($container); + + $this->assertInstanceOf('stdClass', $loader->load('stdClass')); + } + + /** + * @requires PHP 5.3 + */ + public function testLoadUnknownRuntimeReturnsNull() + { + $container = $this->getMockBuilder('Psr\Container\ContainerInterface')->getMock(); + $container->expects($this->once())->method('has')->with('Foo'); + $container->expects($this->never())->method('get'); + + $loader = new Twig_ContainerRuntimeLoader($container); + $this->assertNull($loader->load('Foo')); + } +} diff --git a/lib/silex/vendor/twig/twig/test/Twig/Tests/CustomExtensionTest.php b/lib/silex/vendor/twig/twig/test/Twig/Tests/CustomExtensionTest.php new file mode 100644 index 000000000..f9d44fda5 --- /dev/null +++ b/lib/silex/vendor/twig/twig/test/Twig/Tests/CustomExtensionTest.php @@ -0,0 +1,93 @@ +expectException('InvalidArgumentException'); + $this->expectExceptionMessage($expectedExceptionMessage); + } else { + $this->setExpectedException('InvalidArgumentException', $expectedExceptionMessage); + } + + $env = new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock()); + $env->addExtension($extension); + $env->getUnaryOperators(); + } + + public function provideInvalidExtensions() + { + return array( + array(new InvalidOperatorExtension(new stdClass()), '"InvalidOperatorExtension::getOperators()" must return an array with operators, got "stdClass".'), + array(new InvalidOperatorExtension(array(1, 2, 3)), '"InvalidOperatorExtension::getOperators()" must return an array of 2 elements, got 3.'), + ); + } +} + +class InvalidOperatorExtension implements Twig_ExtensionInterface +{ + private $operators; + + public function __construct($operators) + { + $this->operators = $operators; + } + + public function initRuntime(Twig_Environment $environment) + { + } + + public function getTokenParsers() + { + return array(); + } + + public function getNodeVisitors() + { + return array(); + } + + public function getFilters() + { + return array(); + } + + public function getTests() + { + return array(); + } + + public function getFunctions() + { + return array(); + } + + public function getGlobals() + { + return array(); + } + + public function getOperators() + { + return $this->operators; + } + + public function getName() + { + return __CLASS__; + } +} diff --git a/lib/silex/vendor/twig/twig/test/Twig/Tests/EnvironmentTest.php b/lib/silex/vendor/twig/twig/test/Twig/Tests/EnvironmentTest.php index e4bef74f7..ca9f2cf8a 100644 --- a/lib/silex/vendor/twig/twig/test/Twig/Tests/EnvironmentTest.php +++ b/lib/silex/vendor/twig/twig/test/Twig/Tests/EnvironmentTest.php @@ -9,8 +9,33 @@ * file that was distributed with this source code. */ -class Twig_Tests_EnvironmentTest extends PHPUnit_Framework_TestCase +require_once dirname(__FILE__).'/FilesystemHelper.php'; + +class Twig_Tests_EnvironmentTest extends \PHPUnit\Framework\TestCase { + private $deprecations = array(); + + /** + * @group legacy + */ + public function testLegacyTokenizeSignature() + { + $env = new Twig_Environment(); + $stream = $env->tokenize('{{ foo }}', 'foo'); + $this->assertEquals('{{ foo }}', $stream->getSource()); + $this->assertEquals('foo', $stream->getFilename()); + } + + /** + * @group legacy + */ + public function testLegacyCompileSourceSignature() + { + $loader = new Twig_Loader_Array(array('foo' => '{{ foo }}')); + $env = new Twig_Environment($loader); + $this->assertContains('getTemplateName', $env->compileSource('{{ foo }}', 'foo')); + } + /** * @expectedException LogicException * @expectedExceptionMessage You must set a loader first. @@ -39,61 +64,68 @@ class Twig_Tests_EnvironmentTest extends PHPUnit_Framework_TestCase $this->assertEquals('foo\x3Cbr\x2F\x20\x3E foo\x3Cbr\x2F\x20\x3E', $twig->render('js', array('bar' => 'foo
'))); } - public function escapingStrategyCallback($filename) + public function escapingStrategyCallback($name) { - return $filename; + return $name; } public function testGlobals() { + // to be removed in 2.0 + $loader = $this->getMockBuilder('Twig_EnvironmentTestLoaderInterface')->getMock(); + //$loader = $this->getMockBuilder(array('Twig_LoaderInterface', 'Twig_SourceContextLoaderInterface'))->getMock(); + $loader->expects($this->any())->method('getSourceContext')->will($this->returnValue(new Twig_Source('', ''))); + // globals can be added after calling getGlobals - $twig = new Twig_Environment($this->getMock('Twig_LoaderInterface')); + + $twig = new Twig_Environment($loader); $twig->addGlobal('foo', 'foo'); $twig->getGlobals(); $twig->addGlobal('foo', 'bar'); $globals = $twig->getGlobals(); $this->assertEquals('bar', $globals['foo']); - // globals can be modified after runtime init - $twig = new Twig_Environment($this->getMock('Twig_LoaderInterface')); + // globals can be modified after a template has been loaded + $twig = new Twig_Environment($loader); $twig->addGlobal('foo', 'foo'); $twig->getGlobals(); - $twig->initRuntime(); + $twig->loadTemplate('index'); $twig->addGlobal('foo', 'bar'); $globals = $twig->getGlobals(); $this->assertEquals('bar', $globals['foo']); // globals can be modified after extensions init - $twig = new Twig_Environment($this->getMock('Twig_LoaderInterface')); - $twig->addGlobal('foo', 'foo'); - $twig->getGlobals(); - $twig->getFunctions(); - $twig->addGlobal('foo', 'bar'); - $globals = $twig->getGlobals(); - $this->assertEquals('bar', $globals['foo']); - - // globals can be modified after extensions and runtime init - $twig = new Twig_Environment($loader = new Twig_Loader_Array(array('index' => '{{foo}}'))); - $twig->addGlobal('foo', 'foo'); - $twig->getGlobals(); - $twig->getFunctions(); - $twig->initRuntime(); - $twig->addGlobal('foo', 'bar'); - $globals = $twig->getGlobals(); - $this->assertEquals('bar', $globals['foo']); - $twig = new Twig_Environment($loader); + $twig->addGlobal('foo', 'foo'); + $twig->getGlobals(); + $twig->getFunctions(); + $twig->addGlobal('foo', 'bar'); + $globals = $twig->getGlobals(); + $this->assertEquals('bar', $globals['foo']); + + // globals can be modified after extensions and a template has been loaded + $arrayLoader = new Twig_Loader_Array(array('index' => '{{foo}}')); + $twig = new Twig_Environment($arrayLoader); + $twig->addGlobal('foo', 'foo'); + $twig->getGlobals(); + $twig->getFunctions(); + $twig->loadTemplate('index'); + $twig->addGlobal('foo', 'bar'); + $globals = $twig->getGlobals(); + $this->assertEquals('bar', $globals['foo']); + + $twig = new Twig_Environment($arrayLoader); $twig->getGlobals(); $twig->addGlobal('foo', 'bar'); $template = $twig->loadTemplate('index'); $this->assertEquals('bar', $template->render(array())); /* to be uncomment in Twig 2.0 - // globals cannot be added after runtime init - $twig = new Twig_Environment($this->getMock('Twig_LoaderInterface')); + // globals cannot be added after a template has been loaded + $twig = new Twig_Environment($loader); $twig->addGlobal('foo', 'foo'); $twig->getGlobals(); - $twig->initRuntime(); + $twig->loadTemplate('index'); try { $twig->addGlobal('bar', 'bar'); $this->fail(); @@ -102,7 +134,7 @@ class Twig_Tests_EnvironmentTest extends PHPUnit_Framework_TestCase } // globals cannot be added after extensions init - $twig = new Twig_Environment($this->getMock('Twig_LoaderInterface')); + $twig = new Twig_Environment($loader); $twig->addGlobal('foo', 'foo'); $twig->getGlobals(); $twig->getFunctions(); @@ -113,12 +145,12 @@ class Twig_Tests_EnvironmentTest extends PHPUnit_Framework_TestCase $this->assertFalse(array_key_exists('bar', $twig->getGlobals())); } - // globals cannot be added after extensions and runtime init - $twig = new Twig_Environment($this->getMock('Twig_LoaderInterface')); + // globals cannot be added after extensions and a template has been loaded + $twig = new Twig_Environment($loader); $twig->addGlobal('foo', 'foo'); $twig->getGlobals(); $twig->getFunctions(); - $twig->initRuntime(); + $twig->loadTemplate('index'); try { $twig->addGlobal('bar', 'bar'); $this->fail(); @@ -126,9 +158,9 @@ class Twig_Tests_EnvironmentTest extends PHPUnit_Framework_TestCase $this->assertFalse(array_key_exists('bar', $twig->getGlobals())); } - // test adding globals after initRuntime without call to getGlobals - $twig = new Twig_Environment($this->getMock('Twig_LoaderInterface')); - $twig->initRuntime(); + // test adding globals after a template has been loaded without call to getGlobals + $twig = new Twig_Environment($loader); + $twig->loadTemplate('index'); try { $twig->addGlobal('bar', 'bar'); $this->fail(); @@ -140,15 +172,14 @@ class Twig_Tests_EnvironmentTest extends PHPUnit_Framework_TestCase public function testExtensionsAreNotInitializedWhenRenderingACompiledTemplate() { - $options = array('cache' => sys_get_temp_dir().'/twig', 'auto_reload' => false, 'debug' => false); + $cache = new Twig_Cache_Filesystem($dir = sys_get_temp_dir().'/twig'); + $options = array('cache' => $cache, 'auto_reload' => false, 'debug' => false); // force compilation $twig = new Twig_Environment($loader = new Twig_Loader_Array(array('index' => '{{ foo }}')), $options); - $cache = $twig->getCacheFilename('index'); - if (!is_dir(dirname($cache))) { - mkdir(dirname($cache), 0777, true); - } - file_put_contents($cache, $twig->compileSource('{{ foo }}', 'index')); + + $key = $cache->generateKey('index', $twig->getTemplateClass('index')); + $cache->write($key, $twig->compileSource(new Twig_Source('{{ foo }}', 'index'))); // check that extensions won't be initialized when rendering a template that is already in the cache $twig = $this @@ -164,12 +195,130 @@ class Twig_Tests_EnvironmentTest extends PHPUnit_Framework_TestCase $output = $twig->render('index', array('foo' => 'bar')); $this->assertEquals('bar', $output); - unlink($cache); + Twig_Tests_FilesystemHelper::removeDir($dir); + } + + public function testAutoReloadCacheMiss() + { + $templateName = __FUNCTION__; + $templateContent = __FUNCTION__; + + $cache = $this->getMockBuilder('Twig_CacheInterface')->getMock(); + $loader = $this->getMockLoader($templateName, $templateContent); + $twig = new Twig_Environment($loader, array('cache' => $cache, 'auto_reload' => true, 'debug' => false)); + + // Cache miss: getTimestamp returns 0 and as a result the load() is + // skipped. + $cache->expects($this->once()) + ->method('generateKey') + ->will($this->returnValue('key')); + $cache->expects($this->once()) + ->method('getTimestamp') + ->will($this->returnValue(0)); + $loader->expects($this->never()) + ->method('isFresh'); + $cache->expects($this->once()) + ->method('write'); + $cache->expects($this->once()) + ->method('load'); + + $twig->loadTemplate($templateName); + } + + public function testAutoReloadCacheHit() + { + $templateName = __FUNCTION__; + $templateContent = __FUNCTION__; + + $cache = $this->getMockBuilder('Twig_CacheInterface')->getMock(); + $loader = $this->getMockLoader($templateName, $templateContent); + $twig = new Twig_Environment($loader, array('cache' => $cache, 'auto_reload' => true, 'debug' => false)); + + $now = time(); + + // Cache hit: getTimestamp returns something > extension timestamps and + // the loader returns true for isFresh(). + $cache->expects($this->once()) + ->method('generateKey') + ->will($this->returnValue('key')); + $cache->expects($this->once()) + ->method('getTimestamp') + ->will($this->returnValue($now)); + $loader->expects($this->once()) + ->method('isFresh') + ->will($this->returnValue(true)); + $cache->expects($this->atLeastOnce()) + ->method('load'); + + $twig->loadTemplate($templateName); + } + + public function testAutoReloadOutdatedCacheHit() + { + $templateName = __FUNCTION__; + $templateContent = __FUNCTION__; + + $cache = $this->getMockBuilder('Twig_CacheInterface')->getMock(); + $loader = $this->getMockLoader($templateName, $templateContent); + $twig = new Twig_Environment($loader, array('cache' => $cache, 'auto_reload' => true, 'debug' => false)); + + $now = time(); + + $cache->expects($this->once()) + ->method('generateKey') + ->will($this->returnValue('key')); + $cache->expects($this->once()) + ->method('getTimestamp') + ->will($this->returnValue($now)); + $loader->expects($this->once()) + ->method('isFresh') + ->will($this->returnValue(false)); + $cache->expects($this->once()) + ->method('write'); + $cache->expects($this->once()) + ->method('load'); + + $twig->loadTemplate($templateName); + } + + /** + * @group legacy + */ + public function testHasGetExtensionWithDynamicName() + { + $twig = new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock()); + + $ext1 = new Twig_Tests_EnvironmentTest_Extension_DynamicWithDeprecatedName('ext1'); + $ext2 = new Twig_Tests_EnvironmentTest_Extension_DynamicWithDeprecatedName('ext2'); + $twig->addExtension($ext1); + $twig->addExtension($ext2); + + $this->assertTrue($twig->hasExtension('ext1')); + $this->assertTrue($twig->hasExtension('ext2')); + + $this->assertTrue($twig->hasExtension('Twig_Tests_EnvironmentTest_Extension_DynamicWithDeprecatedName')); + + $this->assertSame($ext1, $twig->getExtension('ext1')); + $this->assertSame($ext2, $twig->getExtension('ext2')); + } + + public function testHasGetExtensionByClassName() + { + $twig = new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock()); + $twig->addExtension($ext = new Twig_Tests_EnvironmentTest_Extension()); + $this->assertTrue($twig->hasExtension('Twig_Tests_EnvironmentTest_Extension')); + $this->assertTrue($twig->hasExtension('\Twig_Tests_EnvironmentTest_Extension')); + + $this->assertSame($ext, $twig->getExtension('Twig_Tests_EnvironmentTest_Extension')); + $this->assertSame($ext, $twig->getExtension('\Twig_Tests_EnvironmentTest_Extension')); + + $this->assertTrue($twig->hasExtension('Twig\Tests\EnvironmentTest\Extension')); + $this->assertSame($ext, $twig->getExtension('Twig\Tests\EnvironmentTest\Extension')); } public function testAddExtension() { - $twig = new Twig_Environment($this->getMock('Twig_LoaderInterface')); + $twig = new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock()); $twig->addExtension(new Twig_Tests_EnvironmentTest_Extension()); $this->assertArrayHasKey('test', $twig->getTags()); @@ -180,7 +329,32 @@ class Twig_Tests_EnvironmentTest extends PHPUnit_Framework_TestCase $this->assertArrayHasKey('foo_binary', $twig->getBinaryOperators()); $this->assertArrayHasKey('foo_global', $twig->getGlobals()); $visitors = $twig->getNodeVisitors(); - $this->assertEquals('Twig_Tests_EnvironmentTest_NodeVisitor', get_class($visitors[2])); + $found = false; + foreach ($visitors as $visitor) { + if ($visitor instanceof Twig_Tests_EnvironmentTest_NodeVisitor) { + $found = true; + } + } + $this->assertTrue($found); + } + + /** + * @requires PHP 5.3 + */ + public function testAddExtensionWithDeprecatedGetGlobals() + { + $twig = new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock()); + $twig->addExtension(new Twig_Tests_EnvironmentTest_Extension_WithGlobals()); + + $this->deprecations = array(); + set_error_handler(array($this, 'handleError')); + + $this->assertArrayHasKey('foo_global', $twig->getGlobals()); + + $this->assertCount(1, $this->deprecations); + $this->assertContains('Defining the getGlobals() method in the "Twig_Tests_EnvironmentTest_Extension_WithGlobals" extension ', $this->deprecations[0]); + + restore_error_handler(); } /** @@ -188,22 +362,180 @@ class Twig_Tests_EnvironmentTest extends PHPUnit_Framework_TestCase */ public function testRemoveExtension() { - $twig = new Twig_Environment($this->getMock('Twig_LoaderInterface')); - $twig->addExtension(new Twig_Tests_EnvironmentTest_Extension()); + $twig = new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock()); + $twig->addExtension(new Twig_Tests_EnvironmentTest_Extension_WithDeprecatedName()); $twig->removeExtension('environment_test'); - $this->assertFalse(array_key_exists('test', $twig->getTags())); - $this->assertFalse(array_key_exists('foo_filter', $twig->getFilters())); - $this->assertFalse(array_key_exists('foo_function', $twig->getFunctions())); - $this->assertFalse(array_key_exists('foo_test', $twig->getTests())); - $this->assertFalse(array_key_exists('foo_unary', $twig->getUnaryOperators())); - $this->assertFalse(array_key_exists('foo_binary', $twig->getBinaryOperators())); - $this->assertFalse(array_key_exists('foo_global', $twig->getGlobals())); + $this->assertArrayNotHasKey('test', $twig->getTags()); + $this->assertArrayNotHasKey('foo_filter', $twig->getFilters()); + $this->assertArrayNotHasKey('foo_function', $twig->getFunctions()); + $this->assertArrayNotHasKey('foo_test', $twig->getTests()); + $this->assertArrayNotHasKey('foo_unary', $twig->getUnaryOperators()); + $this->assertArrayNotHasKey('foo_binary', $twig->getBinaryOperators()); + $this->assertArrayNotHasKey('foo_global', $twig->getGlobals()); $this->assertCount(2, $twig->getNodeVisitors()); } + + public function testAddMockExtension() + { + // should be replaced by the following in 2.0 (this current code is just to avoid a dep notice) + // $extension = $this->getMockBuilder('Twig_Extension')->getMock(); + $extension = eval(<< 'hey')); + + $twig = new Twig_Environment($loader); + $twig->addExtension($extension); + + $this->assertInstanceOf('Twig_ExtensionInterface', $twig->getExtension(get_class($extension))); + $this->assertTrue($twig->isTemplateFresh('page', time())); + } + + public function testInitRuntimeWithAnExtensionUsingInitRuntimeNoDeprecation() + { + $twig = new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock()); + $twig->addExtension(new Twig_Tests_EnvironmentTest_ExtensionWithoutDeprecationInitRuntime()); + $twig->initRuntime(); + + // add a dummy assertion here to satisfy PHPUnit, the only thing we want to test is that the code above + // can be executed without throwing any deprecations + $this->addToAssertionCount(1); + } + + /** + * @requires PHP 5.3 + */ + public function testInitRuntimeWithAnExtensionUsingInitRuntimeDeprecation() + { + $twig = new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock()); + $twig->addExtension(new Twig_Tests_EnvironmentTest_ExtensionWithDeprecationInitRuntime()); + + $this->deprecations = array(); + set_error_handler(array($this, 'handleError')); + + $twig->initRuntime(); + + $this->assertCount(1, $this->deprecations); + $this->assertContains('Defining the initRuntime() method in the "Twig_Tests_EnvironmentTest_ExtensionWithDeprecationInitRuntime" extension is deprecated since version 1.23.', $this->deprecations[0]); + + restore_error_handler(); + } + + public function handleError($type, $msg) + { + if (E_USER_DEPRECATED === $type) { + $this->deprecations[] = $msg; + } + } + + /** + * @requires PHP 5.3 + */ + public function testOverrideExtension() + { + $twig = new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock()); + $twig->addExtension(new Twig_Tests_EnvironmentTest_ExtensionWithDeprecationInitRuntime()); + + $this->deprecations = array(); + set_error_handler(array($this, 'handleError')); + + $twig->addExtension(new Twig_Tests_EnvironmentTest_Extension_WithDeprecatedName()); + $twig->addExtension(new Twig_Tests_EnvironmentTest_Extension_WithDeprecatedName()); + + $this->assertCount(1, $this->deprecations); + $this->assertContains('The possibility to register the same extension twice', $this->deprecations[0]); + + restore_error_handler(); + } + + public function testAddRuntimeLoader() + { + $runtimeLoader = $this->getMockBuilder('Twig_RuntimeLoaderInterface')->getMock(); + $runtimeLoader->expects($this->any())->method('load')->will($this->returnValue(new Twig_Tests_EnvironmentTest_Runtime())); + + $loader = new Twig_Loader_Array(array( + 'func_array' => '{{ from_runtime_array("foo") }}', + 'func_array_default' => '{{ from_runtime_array() }}', + 'func_array_named_args' => '{{ from_runtime_array(name="foo") }}', + 'func_string' => '{{ from_runtime_string("foo") }}', + 'func_string_default' => '{{ from_runtime_string() }}', + 'func_string_named_args' => '{{ from_runtime_string(name="foo") }}', + )); + + $twig = new Twig_Environment($loader); + $twig->addExtension(new Twig_Tests_EnvironmentTest_ExtensionWithoutRuntime()); + $twig->addRuntimeLoader($runtimeLoader); + + $this->assertEquals('foo', $twig->render('func_array')); + $this->assertEquals('bar', $twig->render('func_array_default')); + $this->assertEquals('foo', $twig->render('func_array_named_args')); + $this->assertEquals('foo', $twig->render('func_string')); + $this->assertEquals('bar', $twig->render('func_string_default')); + $this->assertEquals('foo', $twig->render('func_string_named_args')); + } + + /** + * @expectedException Twig_Error_Runtime + * @expectedExceptionMessage Circular reference detected for Twig template "base.html.twig", path: base.html.twig -> base.html.twig in "base.html.twig" at line 1 + */ + public function testFailLoadTemplateOnCircularReference() + { + $twig = new Twig_Environment(new Twig_Loader_Array(array( + 'base.html.twig' => '{% extends "base.html.twig" %}', + ))); + + $twig->loadTemplate('base.html.twig'); + } + + /** + * @expectedException Twig_Error_Runtime + * @expectedExceptionMessage Circular reference detected for Twig template "base1.html.twig", path: base1.html.twig -> base2.html.twig -> base1.html.twig in "base1.html.twig" at line 1 + */ + public function testFailLoadTemplateOnComplexCircularReference() + { + $twig = new Twig_Environment(new Twig_Loader_Array(array( + 'base1.html.twig' => '{% extends "base2.html.twig" %}', + 'base2.html.twig' => '{% extends "base1.html.twig" %}', + ))); + + $twig->loadTemplate('base1.html.twig'); + } + + protected function getMockLoader($templateName, $templateContent) + { + // to be removed in 2.0 + $loader = $this->getMockBuilder('Twig_EnvironmentTestLoaderInterface')->getMock(); + //$loader = $this->getMockBuilder(array('Twig_LoaderInterface', 'Twig_SourceContextLoaderInterface'))->getMock(); + $loader->expects($this->any()) + ->method('getSourceContext') + ->with($templateName) + ->will($this->returnValue(new Twig_Source($templateContent, $templateName))); + $loader->expects($this->any()) + ->method('getCacheKey') + ->with($templateName) + ->will($this->returnValue($templateName)); + + return $loader; + } } -class Twig_Tests_EnvironmentTest_Extension extends Twig_Extension +class Twig_Tests_EnvironmentTest_Extension_WithGlobals extends Twig_Extension +{ + public function getGlobals() + { + return array( + 'foo_global' => 'foo_global', + ); + } +} + +class Twig_Tests_EnvironmentTest_Extension extends Twig_Extension implements Twig_Extension_GlobalsInterface { public function getTokenParsers() { @@ -254,13 +586,32 @@ class Twig_Tests_EnvironmentTest_Extension extends Twig_Extension 'foo_global' => 'foo_global', ); } +} +class_alias('Twig_Tests_EnvironmentTest_Extension', 'Twig\Tests\EnvironmentTest\Extension', false); +class Twig_Tests_EnvironmentTest_Extension_WithDeprecatedName extends Twig_Extension +{ public function getName() { return 'environment_test'; } } +class Twig_Tests_EnvironmentTest_Extension_DynamicWithDeprecatedName extends Twig_Extension +{ + private $name; + + public function __construct($name) + { + $this->name = $name; + } + + public function getName() + { + return $this->name; + } +} + class Twig_Tests_EnvironmentTest_TokenParser extends Twig_TokenParser { public function parse(Twig_Token $token) @@ -290,3 +641,46 @@ class Twig_Tests_EnvironmentTest_NodeVisitor implements Twig_NodeVisitorInterfac return 0; } } + +class Twig_Tests_EnvironmentTest_ExtensionWithDeprecationInitRuntime extends Twig_Extension +{ + public function initRuntime(Twig_Environment $env) + { + } +} + +class Twig_Tests_EnvironmentTest_ExtensionWithoutDeprecationInitRuntime extends Twig_Extension implements Twig_Extension_InitRuntimeInterface +{ + public function initRuntime(Twig_Environment $env) + { + } +} + +class Twig_Tests_EnvironmentTest_ExtensionWithoutRuntime extends Twig_Extension +{ + public function getFunctions() + { + return array( + new Twig_SimpleFunction('from_runtime_array', array('Twig_Tests_EnvironmentTest_Runtime', 'fromRuntime')), + new Twig_SimpleFunction('from_runtime_string', 'Twig_Tests_EnvironmentTest_Runtime::fromRuntime'), + ); + } + + public function getName() + { + return 'from_runtime'; + } +} + +class Twig_Tests_EnvironmentTest_Runtime +{ + public function fromRuntime($name = 'bar') + { + return $name; + } +} + +// to be removed in 2.0 +interface Twig_EnvironmentTestLoaderInterface extends Twig_LoaderInterface, Twig_SourceContextLoaderInterface +{ +} diff --git a/lib/silex/vendor/twig/twig/test/Twig/Tests/ErrorTest.php b/lib/silex/vendor/twig/twig/test/Twig/Tests/ErrorTest.php index d58c40b8d..3ec572ab2 100644 --- a/lib/silex/vendor/twig/twig/test/Twig/Tests/ErrorTest.php +++ b/lib/silex/vendor/twig/twig/test/Twig/Tests/ErrorTest.php @@ -9,12 +9,12 @@ * file that was distributed with this source code. */ -class Twig_Tests_ErrorTest extends PHPUnit_Framework_TestCase +class Twig_Tests_ErrorTest extends \PHPUnit\Framework\TestCase { public function testErrorWithObjectFilename() { $error = new Twig_Error('foo'); - $error->setTemplateFile(new SplFileInfo(__FILE__)); + $error->setSourceContext(new Twig_Source('', new SplFileInfo(__FILE__))); $this->assertContains('test'.DIRECTORY_SEPARATOR.'Twig'.DIRECTORY_SEPARATOR.'Tests'.DIRECTORY_SEPARATOR.'ErrorTest.php', $error->getMessage()); } @@ -22,12 +22,68 @@ class Twig_Tests_ErrorTest extends PHPUnit_Framework_TestCase public function testErrorWithArrayFilename() { $error = new Twig_Error('foo'); - $error->setTemplateFile(array('foo' => 'bar')); + $error->setSourceContext(new Twig_Source('', array('foo' => 'bar'))); $this->assertEquals('foo in {"foo":"bar"}', $error->getMessage()); } - public function testTwigExceptionAddsFileAndLineWhenMissingWithInheritanceOnDisk() + public function testTwigExceptionGuessWithMissingVarAndArrayLoader() + { + $loader = new Twig_Loader_Array(array( + 'base.html' => '{% block content %}{% endblock %}', + 'index.html' => << true, 'debug' => true, 'cache' => false)); + + $template = $twig->loadTemplate('index.html'); + try { + $template->render(array()); + + $this->fail(); + } catch (Twig_Error_Runtime $e) { + $this->assertEquals('Variable "foo" does not exist in "index.html" at line 3.', $e->getMessage()); + $this->assertEquals(3, $e->getTemplateLine()); + $this->assertEquals('index.html', $e->getSourceContext()->getName()); + } + } + + public function testTwigExceptionGuessWithExceptionAndArrayLoader() + { + $loader = new Twig_Loader_Array(array( + 'base.html' => '{% block content %}{% endblock %}', + 'index.html' => << true, 'debug' => true, 'cache' => false)); + + $template = $twig->loadTemplate('index.html'); + try { + $template->render(array('foo' => new Twig_Tests_ErrorTest_Foo())); + + $this->fail(); + } catch (Twig_Error_Runtime $e) { + $this->assertEquals('An exception has been thrown during the rendering of a template ("Runtime error...") in "index.html" at line 3.', $e->getMessage()); + $this->assertEquals(3, $e->getTemplateLine()); + $this->assertEquals('index.html', $e->getSourceContext()->getName()); + } + } + + public function testTwigExceptionGuessWithMissingVarAndFilesystemLoader() { $loader = new Twig_Loader_Filesystem(dirname(__FILE__).'/Fixtures/errors'); $twig = new Twig_Environment($loader, array('strict_variables' => true, 'debug' => true, 'cache' => false)); @@ -38,19 +94,30 @@ class Twig_Tests_ErrorTest extends PHPUnit_Framework_TestCase $this->fail(); } catch (Twig_Error_Runtime $e) { - $this->assertEquals('Variable "foo" does not exist in "index.html" at line 3', $e->getMessage()); + $this->assertEquals('Variable "foo" does not exist.', $e->getMessage()); $this->assertEquals(3, $e->getTemplateLine()); - $this->assertEquals('index.html', $e->getTemplateFile()); + $this->assertEquals('index.html', $e->getSourceContext()->getName()); + $this->assertEquals(3, $e->getLine()); + $this->assertEquals(strtr(dirname(__FILE__).'/Fixtures/errors/index.html', '/', DIRECTORY_SEPARATOR), $e->getFile()); } + } + public function testTwigExceptionGuessWithExceptionAndFilesystemLoader() + { + $loader = new Twig_Loader_Filesystem(dirname(__FILE__).'/Fixtures/errors'); + $twig = new Twig_Environment($loader, array('strict_variables' => true, 'debug' => true, 'cache' => false)); + + $template = $twig->loadTemplate('index.html'); try { $template->render(array('foo' => new Twig_Tests_ErrorTest_Foo())); $this->fail(); } catch (Twig_Error_Runtime $e) { - $this->assertEquals('An exception has been thrown during the rendering of a template ("Runtime error...") in "index.html" at line 3.', $e->getMessage()); + $this->assertEquals('An exception has been thrown during the rendering of a template ("Runtime error...").', $e->getMessage()); $this->assertEquals(3, $e->getTemplateLine()); - $this->assertEquals('index.html', $e->getTemplateFile()); + $this->assertEquals('index.html', $e->getSourceContext()->getName()); + $this->assertEquals(3, $e->getLine()); + $this->assertEquals(strtr(dirname(__FILE__).'/Fixtures/errors/index.html', '/', DIRECTORY_SEPARATOR), $e->getFile()); } } @@ -69,9 +136,9 @@ class Twig_Tests_ErrorTest extends PHPUnit_Framework_TestCase $this->fail(); } catch (Twig_Error_Runtime $e) { - $this->assertEquals(sprintf('Variable "foo" does not exist in "%s" at line %d', $name, $line), $e->getMessage()); + $this->assertEquals(sprintf('Variable "foo" does not exist in "%s" at line %d.', $name, $line), $e->getMessage()); $this->assertEquals($line, $e->getTemplateLine()); - $this->assertEquals($name, $e->getTemplateFile()); + $this->assertEquals($name, $e->getSourceContext()->getName()); } try { @@ -81,7 +148,7 @@ class Twig_Tests_ErrorTest extends PHPUnit_Framework_TestCase } catch (Twig_Error_Runtime $e) { $this->assertEquals(sprintf('An exception has been thrown during the rendering of a template ("Runtime error...") in "%s" at line %d.', $name, $line), $e->getMessage()); $this->assertEquals($line, $e->getTemplateLine()); - $this->assertEquals($name, $e->getTemplateFile()); + $this->assertEquals($name, $e->getSourceContext()->getName()); } } diff --git a/lib/silex/vendor/twig/twig/test/Twig/Tests/ExpressionParserTest.php b/lib/silex/vendor/twig/twig/test/Twig/Tests/ExpressionParserTest.php index ff263cf28..3a173f035 100644 --- a/lib/silex/vendor/twig/twig/test/Twig/Tests/ExpressionParserTest.php +++ b/lib/silex/vendor/twig/twig/test/Twig/Tests/ExpressionParserTest.php @@ -9,7 +9,7 @@ * file that was distributed with this source code. */ -class Twig_Tests_ExpressionParserTest extends PHPUnit_Framework_TestCase +class Twig_Tests_ExpressionParserTest extends \PHPUnit\Framework\TestCase { /** * @expectedException Twig_Error_Syntax @@ -17,18 +17,23 @@ class Twig_Tests_ExpressionParserTest extends PHPUnit_Framework_TestCase */ public function testCanOnlyAssignToNames($template) { - $env = new Twig_Environment($this->getMock('Twig_LoaderInterface'), array('cache' => false, 'autoescape' => false)); + $env = new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock(), array('cache' => false, 'autoescape' => false)); $parser = new Twig_Parser($env); - $parser->parse($env->tokenize($template, 'index')); + $parser->parse($env->tokenize(new Twig_Source($template, 'index'))); } public function getFailingTestsForAssignment() { return array( array('{% set false = "foo" %}'), + array('{% set FALSE = "foo" %}'), array('{% set true = "foo" %}'), + array('{% set TRUE = "foo" %}'), array('{% set none = "foo" %}'), + array('{% set NONE = "foo" %}'), + array('{% set null = "foo" %}'), + array('{% set NULL = "foo" %}'), array('{% set 3 = "foo" %}'), array('{% set 1 + 2 = "foo" %}'), array('{% set "bar" = "foo" %}'), @@ -41,8 +46,8 @@ class Twig_Tests_ExpressionParserTest extends PHPUnit_Framework_TestCase */ public function testArrayExpression($template, $expected) { - $env = new Twig_Environment($this->getMock('Twig_LoaderInterface'), array('cache' => false, 'autoescape' => false)); - $stream = $env->tokenize($template, 'index'); + $env = new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock(), array('cache' => false, 'autoescape' => false)); + $stream = $env->tokenize(new Twig_Source($template, '')); $parser = new Twig_Parser($env); $this->assertEquals($expected, $parser->parse($stream)->getNode('body')->getNode(0)->getNode('expr')); @@ -54,10 +59,10 @@ class Twig_Tests_ExpressionParserTest extends PHPUnit_Framework_TestCase */ public function testArraySyntaxError($template) { - $env = new Twig_Environment($this->getMock('Twig_LoaderInterface'), array('cache' => false, 'autoescape' => false)); + $env = new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock(), array('cache' => false, 'autoescape' => false)); $parser = new Twig_Parser($env); - $parser->parse($env->tokenize($template, 'index')); + $parser->parse($env->tokenize(new Twig_Source($template, 'index'))); } public function getFailingTestsForArray() @@ -149,8 +154,8 @@ class Twig_Tests_ExpressionParserTest extends PHPUnit_Framework_TestCase */ public function testStringExpressionDoesNotConcatenateTwoConsecutiveStrings() { - $env = new Twig_Environment($this->getMock('Twig_LoaderInterface'), array('cache' => false, 'autoescape' => false, 'optimizations' => 0)); - $stream = $env->tokenize('{{ "a" "b" }}', 'index'); + $env = new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock(), array('cache' => false, 'autoescape' => false, 'optimizations' => 0)); + $stream = $env->tokenize(new Twig_Source('{{ "a" "b" }}', 'index')); $parser = new Twig_Parser($env); $parser->parse($stream); @@ -161,8 +166,8 @@ class Twig_Tests_ExpressionParserTest extends PHPUnit_Framework_TestCase */ public function testStringExpression($template, $expected) { - $env = new Twig_Environment($this->getMock('Twig_LoaderInterface'), array('cache' => false, 'autoescape' => false, 'optimizations' => 0)); - $stream = $env->tokenize($template, 'index'); + $env = new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock(), array('cache' => false, 'autoescape' => false, 'optimizations' => 0)); + $stream = $env->tokenize(new Twig_Source($template, '')); $parser = new Twig_Parser($env); $this->assertEquals($expected, $parser->parse($stream)->getNode('body')->getNode(0)->getNode('expr')); @@ -220,10 +225,10 @@ class Twig_Tests_ExpressionParserTest extends PHPUnit_Framework_TestCase */ public function testAttributeCallDoesNotSupportNamedArguments() { - $env = new Twig_Environment($this->getMock('Twig_LoaderInterface'), array('cache' => false, 'autoescape' => false)); + $env = new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock(), array('cache' => false, 'autoescape' => false)); $parser = new Twig_Parser($env); - $parser->parse($env->tokenize('{{ foo.bar(name="Foo") }}', 'index')); + $parser->parse($env->tokenize(new Twig_Source('{{ foo.bar(name="Foo") }}', 'index'))); } /** @@ -231,22 +236,22 @@ class Twig_Tests_ExpressionParserTest extends PHPUnit_Framework_TestCase */ public function testMacroCallDoesNotSupportNamedArguments() { - $env = new Twig_Environment($this->getMock('Twig_LoaderInterface'), array('cache' => false, 'autoescape' => false)); + $env = new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock(), array('cache' => false, 'autoescape' => false)); $parser = new Twig_Parser($env); - $parser->parse($env->tokenize('{% from _self import foo %}{% macro foo() %}{% endmacro %}{{ foo(name="Foo") }}', 'index')); + $parser->parse($env->tokenize(new Twig_Source('{% from _self import foo %}{% macro foo() %}{% endmacro %}{{ foo(name="Foo") }}', 'index'))); } /** * @expectedException Twig_Error_Syntax - * @expectedExceptionMessage An argument must be a name. Unexpected token "string" of value "a" ("name" expected) in "index" at line 1 + * @expectedExceptionMessage An argument must be a name. Unexpected token "string" of value "a" ("name" expected) in "index" at line 1. */ public function testMacroDefinitionDoesNotSupportNonNameVariableName() { - $env = new Twig_Environment($this->getMock('Twig_LoaderInterface'), array('cache' => false, 'autoescape' => false)); + $env = new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock(), array('cache' => false, 'autoescape' => false)); $parser = new Twig_Parser($env); - $parser->parse($env->tokenize('{% macro foo("a") %}{% endmacro %}', 'index')); + $parser->parse($env->tokenize(new Twig_Source('{% macro foo("a") %}{% endmacro %}', 'index'))); } /** @@ -256,10 +261,10 @@ class Twig_Tests_ExpressionParserTest extends PHPUnit_Framework_TestCase */ public function testMacroDefinitionDoesNotSupportNonConstantDefaultValues($template) { - $env = new Twig_Environment($this->getMock('Twig_LoaderInterface'), array('cache' => false, 'autoescape' => false)); + $env = new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock(), array('cache' => false, 'autoescape' => false)); $parser = new Twig_Parser($env); - $parser->parse($env->tokenize($template, 'index')); + $parser->parse($env->tokenize(new Twig_Source($template, 'index'))); } public function getMacroDefinitionDoesNotSupportNonConstantDefaultValues() @@ -275,10 +280,14 @@ class Twig_Tests_ExpressionParserTest extends PHPUnit_Framework_TestCase */ public function testMacroDefinitionSupportsConstantDefaultValues($template) { - $env = new Twig_Environment($this->getMock('Twig_LoaderInterface'), array('cache' => false, 'autoescape' => false)); + $env = new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock(), array('cache' => false, 'autoescape' => false)); $parser = new Twig_Parser($env); - $parser->parse($env->tokenize($template, 'index')); + $parser->parse($env->tokenize(new Twig_Source($template, 'index'))); + + // add a dummy assertion here to satisfy PHPUnit, the only thing we want to test is that the code above + // can be executed without throwing any exceptions + $this->addToAssertionCount(1); } public function getMacroDefinitionSupportsConstantDefaultValues() @@ -296,37 +305,73 @@ class Twig_Tests_ExpressionParserTest extends PHPUnit_Framework_TestCase /** * @expectedException Twig_Error_Syntax - * @expectedExceptionMessage The function "cycl" does not exist. Did you mean "cycle" in "index" at line 1 + * @expectedExceptionMessage Unknown "cycl" function. Did you mean "cycle" in "index" at line 1? */ public function testUnknownFunction() { - $env = new Twig_Environment($this->getMock('Twig_LoaderInterface'), array('cache' => false, 'autoescape' => false)); + $env = new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock(), array('cache' => false, 'autoescape' => false)); $parser = new Twig_Parser($env); - $parser->parse($env->tokenize('{{ cycl() }}', 'index')); + $parser->parse($env->tokenize(new Twig_Source('{{ cycl() }}', 'index'))); } /** * @expectedException Twig_Error_Syntax - * @expectedExceptionMessage The filter "lowe" does not exist. Did you mean "lower" in "index" at line 1 + * @expectedExceptionMessage Unknown "foobar" function in "index" at line 1. + */ + public function testUnknownFunctionWithoutSuggestions() + { + $env = new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock(), array('cache' => false, 'autoescape' => false)); + $parser = new Twig_Parser($env); + + $parser->parse($env->tokenize(new Twig_Source('{{ foobar() }}', 'index'))); + } + + /** + * @expectedException Twig_Error_Syntax + * @expectedExceptionMessage Unknown "lowe" filter. Did you mean "lower" in "index" at line 1? */ public function testUnknownFilter() { - $env = new Twig_Environment($this->getMock('Twig_LoaderInterface'), array('cache' => false, 'autoescape' => false)); + $env = new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock(), array('cache' => false, 'autoescape' => false)); $parser = new Twig_Parser($env); - $parser->parse($env->tokenize('{{ 1|lowe }}', 'index')); + $parser->parse($env->tokenize(new Twig_Source('{{ 1|lowe }}', 'index'))); } /** * @expectedException Twig_Error_Syntax - * @expectedExceptionMessage The test "nul" does not exist. Did you mean "null" in "index" at line 1 + * @expectedExceptionMessage Unknown "foobar" filter in "index" at line 1. + */ + public function testUnknownFilterWithoutSuggestions() + { + $env = new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock(), array('cache' => false, 'autoescape' => false)); + $parser = new Twig_Parser($env); + + $parser->parse($env->tokenize(new Twig_Source('{{ 1|foobar }}', 'index'))); + } + + /** + * @expectedException Twig_Error_Syntax + * @expectedExceptionMessage Unknown "nul" test. Did you mean "null" in "index" at line 1 */ public function testUnknownTest() { - $env = new Twig_Environment($this->getMock('Twig_LoaderInterface'), array('cache' => false, 'autoescape' => false)); + $env = new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock(), array('cache' => false, 'autoescape' => false)); + $parser = new Twig_Parser($env); + $stream = $env->tokenize(new Twig_Source('{{ 1 is nul }}', 'index')); + $parser->parse($stream); + } + + /** + * @expectedException Twig_Error_Syntax + * @expectedExceptionMessage Unknown "foobar" test in "index" at line 1. + */ + public function testUnknownTestWithoutSuggestions() + { + $env = new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock(), array('cache' => false, 'autoescape' => false)); $parser = new Twig_Parser($env); - $parser->parse($env->tokenize('{{ 1 is nul }}', 'index')); + $parser->parse($env->tokenize(new Twig_Source('{{ 1 is foobar }}', 'index'))); } } diff --git a/lib/silex/vendor/twig/twig/test/Twig/Tests/Extension/CoreTest.php b/lib/silex/vendor/twig/twig/test/Twig/Tests/Extension/CoreTest.php index a4692c267..ca69b4af6 100644 --- a/lib/silex/vendor/twig/twig/test/Twig/Tests/Extension/CoreTest.php +++ b/lib/silex/vendor/twig/twig/test/Twig/Tests/Extension/CoreTest.php @@ -9,14 +9,14 @@ * file that was distributed with this source code. */ -class Twig_Tests_Extension_CoreTest extends PHPUnit_Framework_TestCase +class Twig_Tests_Extension_CoreTest extends \PHPUnit\Framework\TestCase { /** * @dataProvider getRandomFunctionTestData */ public function testRandomFunction($value, $expectedInArray) { - $env = new Twig_Environment($this->getMock('Twig_LoaderInterface')); + $env = new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock()); for ($i = 0; $i < 100; ++$i) { $this->assertTrue(in_array(twig_random($env, $value), $expectedInArray, true)); // assertContains() would not consider the type @@ -62,18 +62,18 @@ class Twig_Tests_Extension_CoreTest extends PHPUnit_Framework_TestCase $max = mt_getrandmax(); for ($i = 0; $i < 100; ++$i) { - $val = twig_random(new Twig_Environment($this->getMock('Twig_LoaderInterface'))); + $val = twig_random(new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock())); $this->assertTrue(is_int($val) && $val >= 0 && $val <= $max); } } public function testRandomFunctionReturnsAsIs() { - $this->assertSame('', twig_random(new Twig_Environment($this->getMock('Twig_LoaderInterface')), '')); - $this->assertSame('', twig_random(new Twig_Environment($this->getMock('Twig_LoaderInterface'), array('charset' => null)), '')); + $this->assertSame('', twig_random(new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock()), '')); + $this->assertSame('', twig_random(new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock(), array('charset' => null)), '')); $instance = new stdClass(); - $this->assertSame($instance, twig_random(new Twig_Environment($this->getMock('Twig_LoaderInterface')), $instance)); + $this->assertSame($instance, twig_random(new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock()), $instance)); } /** @@ -81,7 +81,7 @@ class Twig_Tests_Extension_CoreTest extends PHPUnit_Framework_TestCase */ public function testRandomFunctionOfEmptyArrayThrowsException() { - twig_random(new Twig_Environment($this->getMock('Twig_LoaderInterface')), array()); + twig_random(new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock()), array()); } public function testRandomFunctionOnNonUTF8String() @@ -90,7 +90,7 @@ class Twig_Tests_Extension_CoreTest extends PHPUnit_Framework_TestCase $this->markTestSkipped('needs iconv or mbstring'); } - $twig = new Twig_Environment($this->getMock('Twig_LoaderInterface')); + $twig = new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock()); $twig->setCharset('ISO-8859-1'); $text = twig_convert_encoding('Äé', 'ISO-8859-1', 'UTF-8'); @@ -106,7 +106,7 @@ class Twig_Tests_Extension_CoreTest extends PHPUnit_Framework_TestCase $this->markTestSkipped('needs iconv or mbstring'); } - $twig = new Twig_Environment($this->getMock('Twig_LoaderInterface')); + $twig = new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock()); $twig->setCharset('ISO-8859-1'); $input = twig_convert_encoding('Äé', 'ISO-8859-1', 'UTF-8'); @@ -115,12 +115,24 @@ class Twig_Tests_Extension_CoreTest extends PHPUnit_Framework_TestCase $this->assertEquals($output, 'éÄ'); } - public function testCustomEscaper() + /** + * @dataProvider provideCustomEscaperCases + */ + public function testCustomEscaper($expected, $string, $strategy) { - $twig = new Twig_Environment($this->getMock('Twig_LoaderInterface')); - $twig->getExtension('core')->setEscaper('foo', 'foo_escaper_for_test'); + $twig = new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock()); + $twig->getExtension('Twig_Extension_Core')->setEscaper('foo', 'foo_escaper_for_test'); - $this->assertEquals('fooUTF-8', twig_escape_filter($twig, 'foo', 'foo')); + $this->assertSame($expected, twig_escape_filter($twig, $string, $strategy)); + } + + public function provideCustomEscaperCases() + { + return array( + array('fooUTF-8', 'foo', 'foo'), + array('UTF-8', null, 'foo'), + array('42UTF-8', 42, 'foo'), + ); } /** @@ -128,25 +140,132 @@ class Twig_Tests_Extension_CoreTest extends PHPUnit_Framework_TestCase */ public function testUnknownCustomEscaper() { - twig_escape_filter(new Twig_Environment($this->getMock('Twig_LoaderInterface')), 'foo', 'bar'); + twig_escape_filter(new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock()), 'foo', 'bar'); } - public function testTwigFirst() + /** + * @dataProvider provideTwigFirstCases + */ + public function testTwigFirst($expected, $input) { - $twig = new Twig_Environment($this->getMock('Twig_LoaderInterface')); - $this->assertEquals('a', twig_first($twig, 'abc')); - $this->assertEquals(1, twig_first($twig, array(1, 2, 3))); - $this->assertSame('', twig_first($twig, null)); - $this->assertSame('', twig_first($twig, '')); + $twig = new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock()); + $this->assertSame($expected, twig_first($twig, $input)); } - public function testTwigLast() + public function provideTwigFirstCases() { - $twig = new Twig_Environment($this->getMock('Twig_LoaderInterface')); - $this->assertEquals('c', twig_last($twig, 'abc')); - $this->assertEquals(3, twig_last($twig, array(1, 2, 3))); - $this->assertSame('', twig_last($twig, null)); - $this->assertSame('', twig_last($twig, '')); + $i = array(1 => 'a', 2 => 'b', 3 => 'c'); + + return array( + array('a', 'abc'), + array(1, array(1, 2, 3)), + array('', null), + array('', ''), + array('a', new CoreTestIterator($i, array_keys($i), true, 3)), + ); + } + + /** + * @dataProvider provideTwigLastCases + */ + public function testTwigLast($expected, $input) + { + $twig = new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock()); + $this->assertSame($expected, twig_last($twig, $input)); + } + + public function provideTwigLastCases() + { + $i = array(1 => 'a', 2 => 'b', 3 => 'c'); + + return array( + array('c', 'abc'), + array(3, array(1, 2, 3)), + array('', null), + array('', ''), + array('c', new CoreTestIterator($i, array_keys($i), true)), + ); + } + + /** + * @dataProvider provideArrayKeyCases + */ + public function testArrayKeysFilter(array $expected, $input) + { + $this->assertSame($expected, twig_get_array_keys_filter($input)); + } + + public function provideArrayKeyCases() + { + $array = array('a' => 'a1', 'b' => 'b1', 'c' => 'c1'); + $keys = array_keys($array); + + return array( + array($keys, $array), + array($keys, new CoreTestIterator($array, $keys)), + array($keys, new CoreTestIteratorAggregate($array, $keys)), + array($keys, new CoreTestIteratorAggregateAggregate($array, $keys)), + array(array(), null), + array(array('a'), new SimpleXMLElement('')), + ); + } + + /** + * @dataProvider provideInFilterCases + */ + public function testInFilter($expected, $value, $compare) + { + $this->assertSame($expected, twig_in_filter($value, $compare)); + } + + public function provideInFilterCases() + { + $array = array(1, 2, 'a' => 3, 5, 6, 7); + $keys = array_keys($array); + + return array( + array(true, 1, $array), + array(true, '3', $array), + array(true, '3', 'abc3def'), + array(true, 1, new CoreTestIterator($array, $keys, true, 1)), + array(true, '3', new CoreTestIterator($array, $keys, true, 3)), + array(true, '3', new CoreTestIteratorAggregateAggregate($array, $keys, true, 3)), + array(false, 4, $array), + array(false, 4, new CoreTestIterator($array, $keys, true)), + array(false, 4, new CoreTestIteratorAggregateAggregate($array, $keys, true)), + array(false, 1, 1), + array(true, 'b', new SimpleXMLElement('b')), + ); + } + + /** + * @dataProvider provideSliceFilterCases + */ + public function testSliceFilter($expected, $input, $start, $length = null, $preserveKeys = false) + { + $twig = new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock()); + $this->assertSame($expected, twig_slice($twig, $input, $start, $length, $preserveKeys)); + } + + public function provideSliceFilterCases() + { + $i = array('a' => 1, 'b' => 2, 'c' => 3, 'd' => 4); + $keys = array_keys($i); + + return array( + array(array('a' => 1), $i, 0, 1, true), + array(array('a' => 1), $i, 0, 1, false), + array(array('b' => 2, 'c' => 3), $i, 1, 2), + array(array(1), array(1, 2, 3, 4), 0, 1), + array(array(2, 3), array(1, 2, 3, 4), 1, 2), + array(array(2, 3), new CoreTestIterator($i, $keys, true), 1, 2), + array(array('c' => 3, 'd' => 4), new CoreTestIteratorAggregate($i, $keys, true), 2, null, true), + array($i, new CoreTestIterator($i, $keys, true), 0, count($keys) + 10, true), + array(array(), new CoreTestIterator($i, $keys, true), count($keys) + 10), + array('de', 'abcdef', 3, 2), + array(array(), new SimpleXMLElement('12'), 3), + array(array(), new ArrayIterator(array(1, 2)), 3), + ); } } @@ -154,3 +273,83 @@ function foo_escaper_for_test(Twig_Environment $env, $string, $charset) { return $string.$charset; } + +final class CoreTestIteratorAggregate implements IteratorAggregate +{ + private $iterator; + + public function __construct(array $array, array $keys, $allowAccess = false, $maxPosition = false) + { + $this->iterator = new CoreTestIterator($array, $keys, $allowAccess, $maxPosition); + } + + public function getIterator() + { + return $this->iterator; + } +} + +final class CoreTestIteratorAggregateAggregate implements IteratorAggregate +{ + private $iterator; + + public function __construct(array $array, array $keys, $allowValueAccess = false, $maxPosition = false) + { + $this->iterator = new CoreTestIteratorAggregate($array, $keys, $allowValueAccess, $maxPosition); + } + + public function getIterator() + { + return $this->iterator; + } +} + +final class CoreTestIterator implements Iterator +{ + private $position; + private $array; + private $arrayKeys; + private $allowValueAccess; + private $maxPosition; + + public function __construct(array $values, array $keys, $allowValueAccess = false, $maxPosition = false) + { + $this->array = $values; + $this->arrayKeys = $keys; + $this->position = 0; + $this->allowValueAccess = $allowValueAccess; + $this->maxPosition = false === $maxPosition ? count($values) + 1 : $maxPosition; + } + + public function rewind() + { + $this->position = 0; + } + + public function current() + { + if ($this->allowValueAccess) { + return $this->array[$this->key()]; + } + + throw new LogicException('Code should only use the keys, not the values provided by iterator.'); + } + + public function key() + { + return $this->arrayKeys[$this->position]; + } + + public function next() + { + ++$this->position; + if ($this->position === $this->maxPosition) { + throw new LogicException(sprintf('Code should not iterate beyond %d.', $this->maxPosition)); + } + } + + public function valid() + { + return isset($this->arrayKeys[$this->position]); + } +} diff --git a/lib/silex/vendor/twig/twig/test/Twig/Tests/Extension/SandboxTest.php b/lib/silex/vendor/twig/twig/test/Twig/Tests/Extension/SandboxTest.php index d21fb231b..9d90e0ea7 100644 --- a/lib/silex/vendor/twig/twig/test/Twig/Tests/Extension/SandboxTest.php +++ b/lib/silex/vendor/twig/twig/test/Twig/Tests/Extension/SandboxTest.php @@ -9,11 +9,12 @@ * file that was distributed with this source code. */ -class Twig_Tests_Extension_SandboxTest extends PHPUnit_Framework_TestCase +class Twig_Tests_Extension_SandboxTest extends \PHPUnit\Framework\TestCase { - protected static $params, $templates; + protected static $params; + protected static $templates; - public function setUp() + protected function setUp() { self::$params = array( 'name' => 'Fabien', @@ -34,6 +35,7 @@ class Twig_Tests_Extension_SandboxTest extends PHPUnit_Framework_TestCase '1_basic' => '{% if obj.foo %}{{ obj.foo|upper }}{% endif %}', '1_layout' => '{% block content %}{% endblock %}', '1_child' => "{% extends \"1_layout\" %}\n{% block content %}\n{{ \"a\"|json_encode }}\n{% endblock %}", + '1_include' => '{{ include("1_basic1", sandboxed=true) }}', ); } @@ -51,83 +53,146 @@ class Twig_Tests_Extension_SandboxTest extends PHPUnit_Framework_TestCase { $twig = $this->getEnvironment(false, array(), self::$templates); $this->assertEquals('FOO', $twig->loadTemplate('1_basic')->render(self::$params), 'Sandbox does nothing if it is disabled globally'); + } + public function testSandboxUnallowedMethodAccessor() + { $twig = $this->getEnvironment(true, array(), self::$templates); try { $twig->loadTemplate('1_basic1')->render(self::$params); $this->fail('Sandbox throws a SecurityError exception if an unallowed method is called'); } catch (Twig_Sandbox_SecurityError $e) { + $this->assertInstanceOf('Twig_Sandbox_SecurityNotAllowedMethodError', $e, 'Exception should be an instance of Twig_Sandbox_SecurityNotAllowedMethodError'); + $this->assertEquals('FooObject', $e->getClassName(), 'Exception should be raised on the "FooObject" class'); + $this->assertEquals('foo', $e->getMethodName(), 'Exception should be raised on the "foo" method'); } + } + public function testSandboxUnallowedFilter() + { $twig = $this->getEnvironment(true, array(), self::$templates); try { $twig->loadTemplate('1_basic2')->render(self::$params); $this->fail('Sandbox throws a SecurityError exception if an unallowed filter is called'); } catch (Twig_Sandbox_SecurityError $e) { + $this->assertInstanceOf('Twig_Sandbox_SecurityNotAllowedFilterError', $e, 'Exception should be an instance of Twig_Sandbox_SecurityNotAllowedFilterError'); + $this->assertEquals('upper', $e->getFilterName(), 'Exception should be raised on the "upper" filter'); } + } + public function testSandboxUnallowedTag() + { $twig = $this->getEnvironment(true, array(), self::$templates); try { $twig->loadTemplate('1_basic3')->render(self::$params); $this->fail('Sandbox throws a SecurityError exception if an unallowed tag is used in the template'); } catch (Twig_Sandbox_SecurityError $e) { + $this->assertInstanceOf('Twig_Sandbox_SecurityNotAllowedTagError', $e, 'Exception should be an instance of Twig_Sandbox_SecurityNotAllowedTagError'); + $this->assertEquals('if', $e->getTagName(), 'Exception should be raised on the "if" tag'); } + } + public function testSandboxUnallowedProperty() + { $twig = $this->getEnvironment(true, array(), self::$templates); try { $twig->loadTemplate('1_basic4')->render(self::$params); $this->fail('Sandbox throws a SecurityError exception if an unallowed property is called in the template'); } catch (Twig_Sandbox_SecurityError $e) { + $this->assertInstanceOf('Twig_Sandbox_SecurityNotAllowedPropertyError', $e, 'Exception should be an instance of Twig_Sandbox_SecurityNotAllowedPropertyError'); + $this->assertEquals('FooObject', $e->getClassName(), 'Exception should be raised on the "FooObject" class'); + $this->assertEquals('bar', $e->getPropertyName(), 'Exception should be raised on the "bar" property'); } + } + public function testSandboxUnallowedToString() + { $twig = $this->getEnvironment(true, array(), self::$templates); try { $twig->loadTemplate('1_basic5')->render(self::$params); $this->fail('Sandbox throws a SecurityError exception if an unallowed method (__toString()) is called in the template'); } catch (Twig_Sandbox_SecurityError $e) { + $this->assertInstanceOf('Twig_Sandbox_SecurityNotAllowedMethodError', $e, 'Exception should be an instance of Twig_Sandbox_SecurityNotAllowedMethodError'); + $this->assertEquals('FooObject', $e->getClassName(), 'Exception should be raised on the "FooObject" class'); + $this->assertEquals('__tostring', $e->getMethodName(), 'Exception should be raised on the "__toString" method'); } + } + public function testSandboxUnallowedToStringArray() + { $twig = $this->getEnvironment(true, array(), self::$templates); try { $twig->loadTemplate('1_basic6')->render(self::$params); $this->fail('Sandbox throws a SecurityError exception if an unallowed method (__toString()) is called in the template'); } catch (Twig_Sandbox_SecurityError $e) { + $this->assertInstanceOf('Twig_Sandbox_SecurityNotAllowedMethodError', $e, 'Exception should be an instance of Twig_Sandbox_SecurityNotAllowedMethodError'); + $this->assertEquals('FooObject', $e->getClassName(), 'Exception should be raised on the "FooObject" class'); + $this->assertEquals('__tostring', $e->getMethodName(), 'Exception should be raised on the "__toString" method'); } + } + public function testSandboxUnallowedFunction() + { $twig = $this->getEnvironment(true, array(), self::$templates); try { $twig->loadTemplate('1_basic7')->render(self::$params); $this->fail('Sandbox throws a SecurityError exception if an unallowed function is called in the template'); } catch (Twig_Sandbox_SecurityError $e) { + $this->assertInstanceOf('Twig_Sandbox_SecurityNotAllowedFunctionError', $e, 'Exception should be an instance of Twig_Sandbox_SecurityNotAllowedFunctionError'); + $this->assertEquals('cycle', $e->getFunctionName(), 'Exception should be raised on the "cycle" function'); } + } + public function testSandboxAllowMethodFoo() + { $twig = $this->getEnvironment(true, array(), self::$templates, array(), array(), array('FooObject' => 'foo')); FooObject::reset(); $this->assertEquals('foo', $twig->loadTemplate('1_basic1')->render(self::$params), 'Sandbox allow some methods'); $this->assertEquals(1, FooObject::$called['foo'], 'Sandbox only calls method once'); + } + public function testSandboxAllowMethodToString() + { $twig = $this->getEnvironment(true, array(), self::$templates, array(), array(), array('FooObject' => '__toString')); FooObject::reset(); $this->assertEquals('foo', $twig->loadTemplate('1_basic5')->render(self::$params), 'Sandbox allow some methods'); $this->assertEquals(1, FooObject::$called['__toString'], 'Sandbox only calls method once'); + } + public function testSandboxAllowMethodToStringDisabled() + { $twig = $this->getEnvironment(false, array(), self::$templates); FooObject::reset(); $this->assertEquals('foo', $twig->loadTemplate('1_basic5')->render(self::$params), 'Sandbox allows __toString when sandbox disabled'); $this->assertEquals(1, FooObject::$called['__toString'], 'Sandbox only calls method once'); + } + public function testSandboxAllowFilter() + { $twig = $this->getEnvironment(true, array(), self::$templates, array(), array('upper')); $this->assertEquals('FABIEN', $twig->loadTemplate('1_basic2')->render(self::$params), 'Sandbox allow some filters'); + } + public function testSandboxAllowTag() + { $twig = $this->getEnvironment(true, array(), self::$templates, array('if')); $this->assertEquals('foo', $twig->loadTemplate('1_basic3')->render(self::$params), 'Sandbox allow some tags'); + } + public function testSandboxAllowProperty() + { $twig = $this->getEnvironment(true, array(), self::$templates, array(), array(), array(), array('FooObject' => 'bar')); $this->assertEquals('bar', $twig->loadTemplate('1_basic4')->render(self::$params), 'Sandbox allow some properties'); + } + public function testSandboxAllowFunction() + { $twig = $this->getEnvironment(true, array(), self::$templates, array(), array(), array(), array(), array('cycle')); $this->assertEquals('bar', $twig->loadTemplate('1_basic7')->render(self::$params), 'Sandbox allow some functions'); + } + public function testSandboxAllowFunctionsCaseInsensitive() + { foreach (array('getfoobar', 'getFoobar', 'getFooBar') as $name) { $twig = $this->getEnvironment(true, array(), self::$templates, array(), array(), array('FooObject' => $name)); FooObject::reset(); @@ -158,6 +223,8 @@ class Twig_Tests_Extension_SandboxTest extends PHPUnit_Framework_TestCase $twig->loadTemplate('3_basic')->render(self::$params); $this->fail('Sandbox throws a SecurityError exception when the included file is sandboxed'); } catch (Twig_Sandbox_SecurityError $e) { + $this->assertInstanceOf('Twig_Sandbox_SecurityNotAllowedTagError', $e, 'Exception should be an instance of Twig_Sandbox_SecurityNotAllowedTagError'); + $this->assertEquals('sandbox', $e->getTagName()); } } @@ -175,6 +242,23 @@ EOF $this->assertEquals('

username

', $twig->loadTemplate('index')->render(array())); } + public function testSandboxDisabledAfterIncludeFunctionError() + { + $twig = $this->getEnvironment(false, array(), self::$templates); + + $e = null; + try { + $twig->loadTemplate('1_include')->render(self::$params); + } catch (Throwable $e) { + } catch (Exception $e) { + } + if ($e === null) { + $this->fail('An exception should be thrown for this test to be valid.'); + } + + $this->assertFalse($twig->getExtension('Twig_Extension_Sandbox')->isSandboxed(), 'Sandboxed include() function call should not leave Sandbox enabled when an error occurs.'); + } + protected function getEnvironment($sandboxed, $options, $templates, $tags = array(), $filters = array(), $methods = array(), $properties = array(), $functions = array()) { $loader = new Twig_Loader_Array($templates); diff --git a/lib/silex/vendor/twig/twig/test/Twig/Tests/FactoryRuntimeLoaderTest.php b/lib/silex/vendor/twig/twig/test/Twig/Tests/FactoryRuntimeLoaderTest.php new file mode 100644 index 000000000..f97d30694 --- /dev/null +++ b/lib/silex/vendor/twig/twig/test/Twig/Tests/FactoryRuntimeLoaderTest.php @@ -0,0 +1,32 @@ + 'getRuntime')); + + $this->assertInstanceOf('stdClass', $loader->load('stdClass')); + } + + public function testLoadReturnsNullForUnmappedRuntime() + { + $loader = new Twig_FactoryRuntimeLoader(); + + $this->assertNull($loader->load('stdClass')); + } +} + +function getRuntime() +{ + return new stdClass(); +} diff --git a/lib/silex/vendor/twig/twig/test/Twig/Tests/FileCachingTest.php b/lib/silex/vendor/twig/twig/test/Twig/Tests/FileCachingTest.php index 6f5bfee41..e8a23eade 100644 --- a/lib/silex/vendor/twig/twig/test/Twig/Tests/FileCachingTest.php +++ b/lib/silex/vendor/twig/twig/test/Twig/Tests/FileCachingTest.php @@ -9,13 +9,14 @@ * file that was distributed with this source code. */ -class Twig_Tests_FileCachingTest extends PHPUnit_Framework_TestCase -{ - protected $fileName; - protected $env; - protected $tmpDir; +require_once dirname(__FILE__).'/FilesystemHelper.php'; - public function setUp() +class Twig_Tests_FileCachingTest extends \PHPUnit\Framework\TestCase +{ + private $env; + private $tmpDir; + + protected function setUp() { $this->tmpDir = sys_get_temp_dir().'/TwigTests'; if (!file_exists($this->tmpDir)) { @@ -29,51 +30,34 @@ class Twig_Tests_FileCachingTest extends PHPUnit_Framework_TestCase $this->env = new Twig_Environment(new Twig_Loader_Array(array('index' => 'index', 'index2' => 'index2')), array('cache' => $this->tmpDir)); } - public function tearDown() + protected function tearDown() { - if ($this->fileName) { - unlink($this->fileName); - } - - $this->removeDir($this->tmpDir); + Twig_Tests_FilesystemHelper::removeDir($this->tmpDir); } + /** + * @group legacy + */ public function testWritingCacheFiles() { $name = 'index'; $this->env->loadTemplate($name); $cacheFileName = $this->env->getCacheFilename($name); - $this->assertTrue(file_exists($cacheFileName), 'Cache file does not exist.'); - $this->fileName = $cacheFileName; + $this->assertFileExists($cacheFileName, 'Cache file does not exist.'); } + /** + * @group legacy + */ public function testClearingCacheFiles() { $name = 'index2'; $this->env->loadTemplate($name); $cacheFileName = $this->env->getCacheFilename($name); - $this->assertTrue(file_exists($cacheFileName), 'Cache file does not exist.'); + $this->assertFileExists($cacheFileName, 'Cache file does not exist.'); $this->env->clearCacheFiles(); - $this->assertFalse(file_exists($cacheFileName), 'Cache file was not cleared.'); - } - - private function removeDir($target) - { - $fp = opendir($target); - while (false !== $file = readdir($fp)) { - if (in_array($file, array('.', '..'))) { - continue; - } - - if (is_dir($target.'/'.$file)) { - self::removeDir($target.'/'.$file); - } else { - unlink($target.'/'.$file); - } - } - closedir($fp); - rmdir($target); + $this->assertFileNotExists($cacheFileName, 'Cache file was not cleared.'); } } diff --git a/lib/silex/vendor/twig/twig/test/Twig/Tests/FileExtensionEscapingStrategyTest.php b/lib/silex/vendor/twig/twig/test/Twig/Tests/FileExtensionEscapingStrategyTest.php index b310a5be3..a983a47c8 100644 --- a/lib/silex/vendor/twig/twig/test/Twig/Tests/FileExtensionEscapingStrategyTest.php +++ b/lib/silex/vendor/twig/twig/test/Twig/Tests/FileExtensionEscapingStrategyTest.php @@ -9,7 +9,7 @@ * file that was distributed with this source code. */ -class Twig_Tests_FileExtensionEscapingStrategyTest extends PHPUnit_Framework_TestCase +class Twig_Tests_FileExtensionEscapingStrategyTest extends \PHPUnit\Framework\TestCase { /** * @dataProvider getGuessData diff --git a/lib/silex/vendor/twig/twig/test/Twig/Tests/FilesystemHelper.php b/lib/silex/vendor/twig/twig/test/Twig/Tests/FilesystemHelper.php new file mode 100644 index 000000000..0e315e8f7 --- /dev/null +++ b/lib/silex/vendor/twig/twig/test/Twig/Tests/FilesystemHelper.php @@ -0,0 +1,30 @@ + $fileInfo) { + if ($iterator->isDot()) { + continue; + } + + if ($fileInfo->isDir()) { + rmdir($filename); + } else { + unlink($filename); + } + } + rmdir($dir); + } +} diff --git a/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/autoescape/block.test b/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/autoescape/block.test new file mode 100644 index 000000000..1290973a5 --- /dev/null +++ b/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/autoescape/block.test @@ -0,0 +1,21 @@ +--TEST-- +blocks and autoescape +--TEMPLATE-- +{{ include('unrelated.txt.twig') -}} +{{ include('template.html.twig') -}} +--TEMPLATE(unrelated.txt.twig)-- +{% block content %}{% endblock %} +--TEMPLATE(template.html.twig)-- +{% extends 'parent.html.twig' %} +{% block content %} +{{ br -}} +{% endblock %} +--TEMPLATE(parent.html.twig)-- +{% set _content = block('content')|raw %} +{{ _content|raw }} +--DATA-- +return array('br' => '
') +--CONFIG-- +return array('autoescape' => 'name') +--EXPECT-- +<br /> diff --git a/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/autoescape/name.test b/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/autoescape/name.test new file mode 100644 index 000000000..04299bed3 --- /dev/null +++ b/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/autoescape/name.test @@ -0,0 +1,22 @@ +--TEST-- +"name" autoescape strategy +--TEMPLATE-- +{{ br -}} +{{ include('index.js.twig') -}} +{{ include('index.html.twig') -}} +{{ include('index.txt.twig') -}} +--TEMPLATE(index.js.twig)-- +{{ br -}} +--TEMPLATE(index.html.twig)-- +{{ br -}} +--TEMPLATE(index.txt.twig)-- +{{ br -}} +--DATA-- +return array('br' => '
') +--CONFIG-- +return array('autoescape' => 'name') +--EXPECT-- +<br /> +\x3Cbr\x20\x2F\x3E +<br /> +
diff --git a/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/exceptions/child_contents_outside_blocks.test b/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/exceptions/child_contents_outside_blocks.test new file mode 100644 index 000000000..a3f0b50ff --- /dev/null +++ b/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/exceptions/child_contents_outside_blocks.test @@ -0,0 +1,15 @@ +--TEST-- +Exception for child templates defining contents outside blocks defined by parent +--TEMPLATE-- +{% extends 'base.twig' %} + +Content outside a block. + +{% block sidebar %} + Content inside a block. +{% endblock %} +--TEMPLATE(base.twig)-- +{% block sidebar %} +{% endblock %} +--EXCEPTION-- +Twig_Error_Syntax: A template that extends another one cannot include contents outside Twig blocks. Did you forget to put the contents inside a {% block %} tag in "index.twig" at line 3? diff --git a/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/exceptions/multiline_array_with_undefined_variable.test b/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/exceptions/multiline_array_with_undefined_variable.test index ce49165dc..7ff2eedb7 100644 --- a/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/exceptions/multiline_array_with_undefined_variable.test +++ b/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/exceptions/multiline_array_with_undefined_variable.test @@ -15,4 +15,4 @@ Exception for multiline array with undefined variable --DATA-- return array('foobar' => 'foobar') --EXCEPTION-- -Twig_Error_Runtime: Variable "foo2" does not exist in "index.twig" at line 11 +Twig_Error_Runtime: Variable "foo2" does not exist in "index.twig" at line 11. diff --git a/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/exceptions/multiline_array_with_undefined_variable_again.test b/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/exceptions/multiline_array_with_undefined_variable_again.test index e3c040f9c..c425069bf 100644 --- a/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/exceptions/multiline_array_with_undefined_variable_again.test +++ b/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/exceptions/multiline_array_with_undefined_variable_again.test @@ -15,4 +15,4 @@ Exception for multiline array with undefined variable --DATA-- return array() --EXCEPTION-- -Twig_Error_Runtime: Variable "foobar" does not exist in "index.twig" at line 7 +Twig_Error_Runtime: Variable "foobar" does not exist in "index.twig" at line 7. diff --git a/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/exceptions/multiline_function_with_undefined_variable.test b/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/exceptions/multiline_function_with_undefined_variable.test index d799a3906..2f94a5ee9 100644 --- a/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/exceptions/multiline_function_with_undefined_variable.test +++ b/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/exceptions/multiline_function_with_undefined_variable.test @@ -9,4 +9,4 @@ Foo --DATA-- return array() --EXCEPTION-- -Twig_Error_Runtime: Variable "with_context" does not exist in "index.twig" at line 3 +Twig_Error_Runtime: Variable "with_context" does not exist in "index.twig" at line 3. diff --git a/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/exceptions/multiline_tag_with_undefined_variable.test b/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/exceptions/multiline_tag_with_undefined_variable.test index 096a5dbf5..7b3b1daf9 100644 --- a/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/exceptions/multiline_tag_with_undefined_variable.test +++ b/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/exceptions/multiline_tag_with_undefined_variable.test @@ -9,4 +9,4 @@ Foo --DATA-- return array() --EXCEPTION-- -Twig_Error_Runtime: Variable "vars" does not exist in "index.twig" at line 3 +Twig_Error_Runtime: Variable "vars" does not exist in "index.twig" at line 3. diff --git a/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/exceptions/syntax_error_in_reused_template.test b/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/exceptions/syntax_error_in_reused_template.test index 5dd9f3838..9ca418b22 100644 --- a/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/exceptions/syntax_error_in_reused_template.test +++ b/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/exceptions/syntax_error_in_reused_template.test @@ -7,4 +7,4 @@ Exception for syntax error in reused template {% do node.data = 5 %} {% endblock %} --EXCEPTION-- -Twig_Error_Syntax: Unexpected token "operator" of value "=" ("end of statement block" expected) in "foo.twig" at line 3 +Twig_Error_Syntax: Unexpected token "operator" of value "=" ("end of statement block" expected) in "foo.twig" at line 3. diff --git a/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/exceptions/unclosed_tag.test b/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/exceptions/unclosed_tag.test index 02245e931..2c35ad564 100644 --- a/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/exceptions/unclosed_tag.test +++ b/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/exceptions/unclosed_tag.test @@ -17,4 +17,4 @@ Exception for an unclosed tag {% endblock %} --EXCEPTION-- -Twig_Error_Syntax: Unexpected tag name "endblock" (expecting closing tag for the "if" tag defined near line 4) in "index.twig" at line 16 +Twig_Error_Syntax: Unexpected "endblock" tag (expecting closing tag for the "if" tag defined near line 4) in "index.twig" at line 16. diff --git a/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/expressions/_self.test b/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/expressions/_self.test new file mode 100644 index 000000000..32fed8fd0 --- /dev/null +++ b/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/expressions/_self.test @@ -0,0 +1,8 @@ +--TEST-- +_self returns the template name +--TEMPLATE-- +{{ _self }} +--DATA-- +return array() +--EXPECT-- +index.twig diff --git a/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/expressions/magic_call.test b/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/expressions/magic_call.test index 159db96f5..1a27a2d6f 100644 --- a/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/expressions/magic_call.test +++ b/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/expressions/magic_call.test @@ -6,21 +6,21 @@ Twig supports __call() for attributes --DATA-- class TestClassForMagicCallAttributes { - public function getBar() - { - return 'bar_from_getbar'; - } - - public function __call($method, $arguments) - { - if ('foo' === $method) + public function getBar() { - return 'foo_from_call'; + return 'bar_from_getbar'; } - return false; - } + public function __call($method, $arguments) + { + if ('foo' === $method) { + return 'foo_from_call'; + } + + return false; + } } + return array('foo' => new TestClassForMagicCallAttributes()) --EXPECT-- foo_from_call diff --git a/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/expressions/power.test b/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/expressions/power.test new file mode 100644 index 000000000..eacc98f86 --- /dev/null +++ b/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/expressions/power.test @@ -0,0 +1,20 @@ +--TEST-- +Twig parses power expressions +--TEMPLATE-- +{{ 2**3 }} +{{ (-2)**3 }} +{{ (-2)**(-3) }} +{{ a ** a }} +{{ a ** b }} +{{ b ** a }} +{{ b ** b }} +--DATA-- +return array('a' => 4, 'b' => -2); +--EXPECT-- +8 +-8 +-0.125 +256 +0.0625 +16 +0.25 diff --git a/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/expressions/two_word_operators_as_variables.test b/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/expressions/two_word_operators_as_variables.test index 47f37e450..0eaabb446 100644 --- a/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/expressions/two_word_operators_as_variables.test +++ b/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/expressions/two_word_operators_as_variables.test @@ -5,4 +5,4 @@ Twig does not allow to use two-word named operators as variable names --DATA-- return array() --EXCEPTION-- -Twig_Error_Syntax: Unexpected token "operator" of value "starts with" in "index.twig" at line 2 +Twig_Error_Syntax: Unexpected token "operator" of value "starts with" in "index.twig" at line 2. diff --git a/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/date_default_format.test b/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/date_default_format.test index 11a1ef4bd..6ad504cdf 100644 --- a/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/date_default_format.test +++ b/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/date_default_format.test @@ -5,7 +5,7 @@ {{ date1|date('d/m/Y') }} --DATA-- date_default_timezone_set('UTC'); -$twig->getExtension('core')->setDateFormat('Y-m-d', '%d days %h hours'); +$twig->getExtension('Twig_Extension_Core')->setDateFormat('Y-m-d', '%d days %h hours'); return array( 'date1' => mktime(13, 45, 0, 10, 4, 2010), ) diff --git a/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/date_default_format_interval.test b/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/date_default_format_interval.test index e6d3707d9..be4a642e7 100644 --- a/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/date_default_format_interval.test +++ b/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/date_default_format_interval.test @@ -7,7 +7,7 @@ version_compare(phpversion(), '5.3.0', '>=') {{ date2|date('%d days') }} --DATA-- date_default_timezone_set('UTC'); -$twig->getExtension('core')->setDateFormat('Y-m-d', '%d days %h hours'); +$twig->getExtension('Twig_Extension_Core')->setDateFormat('Y-m-d', '%d days %h hours'); return array( 'date2' => new DateInterval('P2D'), ) diff --git a/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/escape_javascript.test b/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/escape_javascript.test new file mode 100644 index 000000000..647147a43 --- /dev/null +++ b/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/escape_javascript.test @@ -0,0 +1,8 @@ +--TEST-- +"escape" filter +--TEMPLATE-- +{{ "é ♜ 𝌆"|e('js') }} +--DATA-- +return array() +--EXPECT-- +\u00E9\x20\u265C\x20\uD834\uDF06 diff --git a/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/length.test b/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/length.test index 3347474d5..a7f1e5031 100644 --- a/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/length.test +++ b/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/length.test @@ -4,11 +4,28 @@ {{ array|length }} {{ string|length }} {{ number|length }} -{{ markup|length }} +{{ to_string_able|length }} +{{ countable|length }} +{{ null|length }} +{{ magic|length }} +{{ non_countable|length }} --DATA-- -return array('array' => array(1, 4), 'string' => 'foo', 'number' => 1000, 'markup' => new Twig_Markup('foo', 'UTF-8')) +return array( + 'array' => array(1, 4), + 'string' => 'foo', + 'number' => 1000, + 'to_string_able' => new ToStringStub('foobar'), + 'countable' => new CountableStub(42), /* also asserts we do *not* call __toString() */ + 'null' => null, + 'magic' => new MagicCallStub(), /* used to assert we do *not* call __call */ + 'non_countable' => new \StdClass(), +); --EXPECT-- 2 3 4 -3 +6 +42 +0 +1 +1 diff --git a/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/number_format_default.test b/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/number_format_default.test index c6903cc73..65c1cdb48 100644 --- a/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/number_format_default.test +++ b/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/number_format_default.test @@ -9,7 +9,7 @@ {{ 1020.25|number_format(2, ',') }} {{ 1020.25|number_format(2, ',', '.') }} --DATA-- -$twig->getExtension('core')->setNumberFormat(2, '!', '='); +$twig->getExtension('Twig_Extension_Core')->setNumberFormat(2, '!', '='); return array(); --EXPECT-- 20!00 diff --git a/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/replace_invalid_arg.test b/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/replace_invalid_arg.test index 08ecfd490..2143a8690 100644 --- a/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/replace_invalid_arg.test +++ b/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/replace_invalid_arg.test @@ -3,6 +3,6 @@ Exception for invalid argument type in replace call --TEMPLATE-- {{ 'test %foo%'|replace(stdClass) }} --DATA-- -return array('stdClass' => new \stdClass()) +return array('stdClass' => new stdClass()) --EXCEPTION-- Twig_Error_Runtime: The "replace" filter expects an array or "Traversable" as replace values, got "stdClass" in "index.twig" at line 2. diff --git a/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/static_calls.test b/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/static_calls.test new file mode 100644 index 000000000..4e17b7726 --- /dev/null +++ b/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/static_calls.test @@ -0,0 +1,10 @@ +--TEST-- +Filters as static method calls +--TEMPLATE-- +{{ 'foo'|static_call_string }} +{{ 'foo'|static_call_array }} +--DATA-- +return array('foo' => 'foo') +--EXPECT-- +*foo* +*foo* diff --git a/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/trim.test b/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/trim.test index 319206258..b1ef7b473 100644 --- a/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/trim.test +++ b/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/trim.test @@ -4,9 +4,21 @@ {{ " I like Twig. "|trim }} {{ text|trim }} {{ " foo/"|trim("/") }} +{{ "xxxI like Twig.xxx"|trim(character_mask="x", side="left") }} +{{ "xxxI like Twig.xxx"|trim(side="right", character_mask="x") }} +{{ "xxxI like Twig.xxx"|trim("x", "right") }} +{{ "/ foo/"|trim("/", "left") }} +{{ "/ foo/"|trim(character_mask="/", side="left") }} +{{ " do nothing. "|trim("", "right") }} --DATA-- return array('text' => " If you have some HTML it will be escaped. ") --EXPECT-- I like Twig. If you have some <strong>HTML</strong> it will be escaped. foo +I like Twig.xxx +xxxI like Twig. +xxxI like Twig. + foo/ + foo/ + do nothing. diff --git a/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/urlencode_deprecated.test b/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/urlencode_deprecated.test index 11800e9c0..35e50390d 100644 --- a/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/urlencode_deprecated.test +++ b/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/urlencode_deprecated.test @@ -1,5 +1,5 @@ --TEST-- -"url_encode" filter for PHP < 5.4 and HHVM +"url_encode" filter for PHP < 5.4 --CONDITION-- defined('PHP_QUERY_RFC3986') --TEMPLATE-- diff --git a/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/functions/block_with_template.test b/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/functions/block_with_template.test new file mode 100644 index 000000000..8305eb67f --- /dev/null +++ b/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/functions/block_with_template.test @@ -0,0 +1,22 @@ +--TEST-- +"block" function with a template argument +--TEMPLATE-- +{{ block('foo', 'included.twig') }} +{{ block('foo', included_loaded) }} +{{ block('foo', included_loaded_internal) }} +{% set output = block('foo', 'included.twig') %} +{{ output }} +{% block foo %}NOT FOO{% endblock %} +--TEMPLATE(included.twig)-- +{% block foo %}FOO{% endblock %} +--DATA-- +return array( + 'included_loaded' => $twig->load('included.twig'), + 'included_loaded_internal' => $twig->loadTemplate('included.twig'), +) +--EXPECT-- +FOO +FOO +FOO +FOO +NOT FOO diff --git a/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/functions/block_without_name.test b/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/functions/block_without_name.test new file mode 100644 index 000000000..665cc87e5 --- /dev/null +++ b/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/functions/block_without_name.test @@ -0,0 +1,12 @@ +--TEST-- +"block" function without arguments +--TEMPLATE-- +{% extends 'base.twig' %} +{% block bar %}BAR{% endblock %} +--TEMPLATE(base.twig)-- +{% block foo %}{{ block() }}{% endblock %} +{% block bar %}BAR_BASE{% endblock %} +--DATA-- +return array() +--EXCEPTION-- +Twig_Error_Syntax: The "block" function takes one argument (the block name) in "base.twig" at line 2. diff --git a/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/functions/date.test b/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/functions/date.test index 8be9c0c40..c9f464468 100644 --- a/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/functions/date.test +++ b/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/functions/date.test @@ -1,12 +1,13 @@ --TEST-- "date" function --TEMPLATE-- -{{ date() == date('now') ? 'OK' : 'KO' }} +{{ date().format('r') == date('now').format('r') ? 'OK' : 'KO' }} {{ date(date1) == date('2010-10-04 13:45') ? 'OK' : 'KO' }} {{ date(date2) == date('2010-10-04 13:45') ? 'OK' : 'KO' }} {{ date(date3) == date('2010-10-04 13:45') ? 'OK' : 'KO' }} {{ date(date4) == date('2010-10-04 13:45') ? 'OK' : 'KO' }} {{ date(date5) == date('1964-01-02 03:04') ? 'OK' : 'KO' }} +{{ date() > date('-1day') ? 'OK' : 'KO' }} --DATA-- date_default_timezone_set('UTC'); return array( @@ -23,3 +24,4 @@ OK OK OK OK +OK diff --git a/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/functions/magic_call.test b/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/functions/magic_call.test new file mode 100644 index 000000000..933544312 --- /dev/null +++ b/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/functions/magic_call.test @@ -0,0 +1,8 @@ +--TEST-- +__call calls +--TEMPLATE-- +{{ 'foo'|magic_call }} +--DATA-- +return array() +--EXPECT-- +magic_foo diff --git a/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/functions/magic_call53.test b/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/functions/magic_call53.test new file mode 100644 index 000000000..a0f55e116 --- /dev/null +++ b/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/functions/magic_call53.test @@ -0,0 +1,12 @@ +--TEST-- +__staticCall calls +--CONDITION-- +version_compare(phpversion(), '5.3.0', '>=') +--TEMPLATE-- +{{ 'foo'|magic_call_string }} +{{ 'foo'|magic_call_array }} +--DATA-- +return array() +--EXPECT-- +static_magic_foo +static_magic_foo diff --git a/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/functions/recursive_block_with_inheritance.test b/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/functions/recursive_block_with_inheritance.test index f39712da8..bf0556d24 100644 --- a/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/functions/recursive_block_with_inheritance.test +++ b/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/functions/recursive_block_with_inheritance.test @@ -12,7 +12,7 @@ {% block list %}
    {{ block('children') }}
{% endblock %} {% block children %}{% set currentItem = item %}{% for item in currentItem %}{{ block('item') }}{% endfor %}{% set item = currentItem %}{% endblock %} {% block item %}
  • {% if item is not iterable %}{{ block('label') }}{% else %}{{ block('list') }}{% endif %}
  • {% endblock %} -{% block label %}{{ item }}{{ block('unknown') }}{% endblock %} +{% block label %}{{ item }}{% endblock %} --TEMPLATE(base.twig)-- {{ block('list') }} --DATA-- diff --git a/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/functions/static_calls.test b/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/functions/static_calls.test new file mode 100644 index 000000000..57e5be392 --- /dev/null +++ b/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/functions/static_calls.test @@ -0,0 +1,10 @@ +--TEST-- +Functions as static method calls +--TEMPLATE-- +{{ static_call_string('foo') }} +{{ static_call_array('foo') }} +--DATA-- +return array('foo' => 'foo') +--EXPECT-- +*foo* +*foo* diff --git a/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/macros/varargs_argument.test b/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/macros/varargs_argument.test index b08c8ec91..800c262e7 100644 --- a/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/macros/varargs_argument.test +++ b/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/macros/varargs_argument.test @@ -4,5 +4,4 @@ macro with varargs argument {% macro test(varargs) %} {% endmacro %} --EXCEPTION-- -Twig_Error_Syntax: The argument "varargs" in macro "test" cannot be defined because the variable "varargs" is reserved for arbitrary arguments in "index.twig" at line 2 - +Twig_Error_Syntax: The argument "varargs" in macro "test" cannot be defined because the variable "varargs" is reserved for arbitrary arguments in "index.twig" at line 2. diff --git a/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/regression/combined_debug_info.test b/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/regression/combined_debug_info.test index df485783a..ff977ad62 100644 --- a/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/regression/combined_debug_info.test +++ b/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/regression/combined_debug_info.test @@ -12,4 +12,4 @@ foo --DATA-- return array('foo' => 'foo'); --EXCEPTION-- -Twig_Error_Runtime: Impossible to access an attribute ("bar") on a string variable ("foo") in "foo" at line 3 +Twig_Error_Runtime: Impossible to access an attribute ("bar") on a string variable ("foo") in "foo" at line 3. diff --git a/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/block/block_unique_name.test b/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/block/block_unique_name.test index 5c205c0aa..bc89ec820 100644 --- a/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/block/block_unique_name.test +++ b/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/block/block_unique_name.test @@ -8,4 +8,4 @@ --DATA-- return array() --EXCEPTION-- -Twig_Error_Syntax: The block 'content' has already been defined line 2 in "index.twig" at line 3 +Twig_Error_Syntax: The block 'content' has already been defined line 2 in "index.twig" at line 3. diff --git a/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/embed/complex_dynamic_parent.test b/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/embed/complex_dynamic_parent.test new file mode 100644 index 000000000..de5ea7eea --- /dev/null +++ b/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/embed/complex_dynamic_parent.test @@ -0,0 +1,35 @@ +--TEST-- +"embed" tag +--TEMPLATE-- +FOO +{% embed foo ~ ".twig" %} + {% block c1 %} + {{ parent() }} + block1extended + {% endblock %} +{% endembed %} + +BAR +--TEMPLATE(foo.twig)-- +A +{% block c1 %} + block1 +{% endblock %} +B +{% block c2 %} + block2 +{% endblock %} +C +--DATA-- +return array('foo' => 'foo') +--EXPECT-- +FOO + +A + block1 + + block1extended + B + block2 +C +BAR diff --git a/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/embed/dynamic_parent.test b/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/embed/dynamic_parent.test new file mode 100644 index 000000000..2a125e6b5 --- /dev/null +++ b/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/embed/dynamic_parent.test @@ -0,0 +1,35 @@ +--TEST-- +"embed" tag +--TEMPLATE-- +FOO +{% embed foo %} + {% block c1 %} + {{ parent() }} + block1extended + {% endblock %} +{% endembed %} + +BAR +--TEMPLATE(foo.twig)-- +A +{% block c1 %} + block1 +{% endblock %} +B +{% block c2 %} + block2 +{% endblock %} +C +--DATA-- +return array('foo' => 'foo.twig') +--EXPECT-- +FOO + +A + block1 + + block1extended + B + block2 +C +BAR diff --git a/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/embed/error_line.test b/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/embed/error_line.test index 71ab2e018..431473707 100644 --- a/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/embed/error_line.test +++ b/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/embed/error_line.test @@ -13,4 +13,4 @@ BAR --DATA-- return array() --EXCEPTION-- -Twig_Error_Runtime: Variable "nothing" does not exist in "index.twig" at line 5 +Twig_Error_Runtime: Variable "nothing" does not exist in "index.twig" at line 5. diff --git a/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/embed/with_extends.test b/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/embed/with_extends.test index cf7953d3e..2c1dd584c 100644 --- a/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/embed/with_extends.test +++ b/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/embed/with_extends.test @@ -17,6 +17,7 @@ block1extended {% endblock %} {% endembed %} + {{ parent() }} {% endblock %} --TEMPLATE(base.twig)-- A @@ -54,4 +55,6 @@ A block1extended B block2 -CB \ No newline at end of file +C blockc2base + +B \ No newline at end of file diff --git a/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/for/loop_not_defined.test b/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/for/loop_not_defined.test index 4301ef2ff..6a2af63b6 100644 --- a/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/for/loop_not_defined.test +++ b/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/for/loop_not_defined.test @@ -7,4 +7,4 @@ --DATA-- return array('items' => array('a', 'b')) --EXCEPTION-- -Twig_Error_Syntax: The "loop.last" variable is not defined when looping with a condition in "index.twig" at line 3 +Twig_Error_Syntax: The "loop.last" variable is not defined when looping with a condition in "index.twig" at line 3. diff --git a/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/for/loop_not_defined_cond.test b/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/for/loop_not_defined_cond.test index c7e723a51..1e819ca0a 100644 --- a/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/for/loop_not_defined_cond.test +++ b/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/for/loop_not_defined_cond.test @@ -6,4 +6,4 @@ --DATA-- return array('items' => array('a', 'b')) --EXCEPTION-- -Twig_Error_Syntax: The "loop" variable cannot be used in a looping condition in "index.twig" at line 2 +Twig_Error_Syntax: The "loop" variable cannot be used in a looping condition in "index.twig" at line 2. diff --git a/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/inheritance/parent_in_a_block.test b/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/inheritance/parent_in_a_block.test index c9e86b1ae..cca6dbc9b 100644 --- a/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/inheritance/parent_in_a_block.test +++ b/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/inheritance/parent_in_a_block.test @@ -5,4 +5,4 @@ {% extends "foo.twig" %} {% endblock %} --EXCEPTION-- -Twig_Error_Syntax: Cannot extend from a block in "index.twig" at line 3 +Twig_Error_Syntax: Cannot extend from a block in "index.twig" at line 3. diff --git a/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/inheritance/parent_without_extends.test b/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/inheritance/parent_without_extends.test index a9eaa4c13..e29b1ac4b 100644 --- a/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/inheritance/parent_without_extends.test +++ b/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/inheritance/parent_without_extends.test @@ -5,4 +5,4 @@ {{ parent() }} {% endblock %} --EXCEPTION-- -Twig_Error_Syntax: Calling "parent" on a template that does not extend nor "use" another template is forbidden in "index.twig" at line 3 +Twig_Error_Syntax: Calling "parent" on a template that does not extend nor "use" another template is forbidden in "index.twig" at line 3. diff --git a/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/macro/from_with_reserved_name.test b/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/macro/from_with_reserved_name.test index 6df4f5d6c..2de9765ff 100644 --- a/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/macro/from_with_reserved_name.test +++ b/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/macro/from_with_reserved_name.test @@ -6,4 +6,4 @@ --DATA-- return array() --EXCEPTION-- -Twig_Error_Syntax: "templateName" cannot be an imported macro as it is a reserved keyword in "index.twig" at line 2 +Twig_Error_Syntax: "templateName" cannot be an imported macro as it is a reserved keyword in "index.twig" at line 2. diff --git a/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/macro/import_with_reserved_nam.test b/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/macro/import_with_reserved_nam.test index e5aa7498c..7bd93c62d 100644 --- a/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/macro/import_with_reserved_nam.test +++ b/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/macro/import_with_reserved_nam.test @@ -8,4 +8,4 @@ --DATA-- return array() --EXCEPTION-- -Twig_Error_Syntax: "parent" cannot be called as macro as it is a reserved keyword in "index.twig" at line 4 +Twig_Error_Syntax: "parent" cannot be called as macro as it is a reserved keyword in "index.twig" at line 4. diff --git a/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/macro/reserved_name.test b/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/macro/reserved_name.test index a2dde5a50..f7c102f02 100644 --- a/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/macro/reserved_name.test +++ b/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/macro/reserved_name.test @@ -7,4 +7,4 @@ --DATA-- return array() --EXCEPTION-- -Twig_Error_Syntax: "parent" cannot be used as a macro name as it is a reserved keyword in "index.twig" at line 2 +Twig_Error_Syntax: "parent" cannot be used as a macro name as it is a reserved keyword in "index.twig" at line 2. diff --git a/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/raw/mixed_usage_with_raw.legacy.test b/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/raw/mixed_usage_with_raw.legacy.test index 2fd9fb26d..99deefc32 100644 --- a/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/raw/mixed_usage_with_raw.legacy.test +++ b/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/raw/mixed_usage_with_raw.legacy.test @@ -7,4 +7,4 @@ --DATA-- return array() --EXCEPTION-- -Twig_Error_Syntax: Unexpected end of file: Unclosed "raw" block in "index.twig" at line 2 +Twig_Error_Syntax: Unexpected end of file: Unclosed "raw" block in "index.twig" at line 2. diff --git a/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/sandbox/not_valid1.test b/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/sandbox/not_valid1.test index 683c59a0e..dfddc1513 100644 --- a/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/sandbox/not_valid1.test +++ b/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/sandbox/not_valid1.test @@ -8,4 +8,4 @@ sandbox tag --TEMPLATE(foo.twig)-- foo --EXCEPTION-- -Twig_Error_Syntax: Only "include" tags are allowed within a "sandbox" section in "index.twig" at line 4 +Twig_Error_Syntax: Only "include" tags are allowed within a "sandbox" section in "index.twig" at line 4. diff --git a/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/sandbox/not_valid2.test b/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/sandbox/not_valid2.test index 3dcfa88c7..a33a13ee6 100644 --- a/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/sandbox/not_valid2.test +++ b/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/sandbox/not_valid2.test @@ -11,4 +11,4 @@ sandbox tag --TEMPLATE(foo.twig)-- foo --EXCEPTION-- -Twig_Error_Syntax: Only "include" tags are allowed within a "sandbox" section in "index.twig" at line 5 +Twig_Error_Syntax: Only "include" tags are allowed within a "sandbox" section in "index.twig" at line 5. diff --git a/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/use/inheritance.test b/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/use/inheritance.test index 6368b08de..0d0d470ee 100644 --- a/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/use/inheritance.test +++ b/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/use/inheritance.test @@ -8,7 +8,7 @@ {% use "ancestor.twig" %} {% block sub_container %} -
    overriden sub_container
    +
    overridden sub_container
    {% endblock %} --TEMPLATE(ancestor.twig)-- {% block container %} @@ -21,5 +21,5 @@ --DATA-- return array() --EXPECT-- -
    overriden sub_container
    +
    overridden sub_container
    diff --git a/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/use/inheritance2.test b/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/use/inheritance2.test index 114e3015e..df95599ce 100644 --- a/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/use/inheritance2.test +++ b/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/use/inheritance2.test @@ -7,7 +7,7 @@ {{ block('container') }} --TEMPLATE(parent.twig)-- {% block sub_container %} -
    overriden sub_container
    +
    overridden sub_container
    {% endblock %} --TEMPLATE(ancestor.twig)-- {% block container %} @@ -20,5 +20,5 @@ --DATA-- return array() --EXPECT-- -
    overriden sub_container
    +
    overridden sub_container
    diff --git a/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/use/use_with_parent.test b/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/use/use_with_parent.test new file mode 100644 index 000000000..3fe2ad83c --- /dev/null +++ b/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/use/use_with_parent.test @@ -0,0 +1,24 @@ +--TEST-- +"use" tag with a parent block +--TEMPLATE-- +{% extends "parent.twig" %} + +{% use 'blocks.twig' %} + +{% block body %} + {{ parent() -}} + CHILD + {{ block('content') }} +{% endblock %} +--TEMPLATE(parent.twig)-- +{% block body %} + PARENT +{% endblock %} +--TEMPLATE(blocks.twig)-- +{% block content 'BLOCK' %} +--DATA-- +return array() +--EXPECT-- +PARENT +CHILD + BLOCK diff --git a/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/verbatim/mixed_usage_with_raw.test b/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/verbatim/mixed_usage_with_raw.test index 941dddcce..28626a816 100644 --- a/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/verbatim/mixed_usage_with_raw.test +++ b/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/verbatim/mixed_usage_with_raw.test @@ -7,4 +7,4 @@ --DATA-- return array() --EXCEPTION-- -Twig_Error_Syntax: Unexpected end of file: Unclosed "verbatim" block in "index.twig" at line 2 +Twig_Error_Syntax: Unexpected end of file: Unclosed "verbatim" block in "index.twig" at line 2. diff --git a/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/with/basic.test b/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/with/basic.test new file mode 100644 index 000000000..264ca5e7e --- /dev/null +++ b/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/with/basic.test @@ -0,0 +1,13 @@ +--TEST-- +"with" tag +--TEMPLATE-- +{% with %} + {% set bar = 'BAZ' %} + {{ foo }}{{ bar }} +{% endwith %} +{{ foo }}{{ bar }} +--DATA-- +return array('foo' => 'foo', 'bar' => 'bar') +--EXPECT-- +fooBAZ +foobar diff --git a/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/with/expression.test b/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/with/expression.test new file mode 100644 index 000000000..32ed0916c --- /dev/null +++ b/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/with/expression.test @@ -0,0 +1,10 @@ +--TEST-- +"with" tag with expression +--TEMPLATE-- +{% with {foo: 'foo', bar: 'BAZ'} %} + {{ foo }}{{ bar }} +{% endwith %} +--DATA-- +return array('foo' => 'baz') +--EXPECT-- +fooBAZ diff --git a/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/with/nested.test b/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/with/nested.test new file mode 100644 index 000000000..98e3aef43 --- /dev/null +++ b/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/with/nested.test @@ -0,0 +1,15 @@ +--TEST-- +nested "with" tags +--TEMPLATE-- +{% set foo, bar = 'foo', 'bar' %} +{% with {bar: 'BAZ'} %} + {% with {foo: 'FOO'} %} + {{ foo }}{{ bar }} + {% endwith %} +{% endwith %} +{{ foo }}{{ bar }} +--DATA-- +return array() +--EXPECT-- +FOOBAZ + foobar diff --git a/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/with/with_no_hash.test b/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/with/with_no_hash.test new file mode 100644 index 000000000..93689f42c --- /dev/null +++ b/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/with/with_no_hash.test @@ -0,0 +1,10 @@ +--TEST-- +"with" tag with an expression that is not a hash +--TEMPLATE-- +{% with vars %} + {{ foo }}{{ bar }} +{% endwith %} +--DATA-- +return array('vars' => 'no-hash') +--EXCEPTION-- +Twig_Error_Runtime: Variables passed to the "with" tag must be a hash in "index.twig" at line 2. diff --git a/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/with/with_only.test b/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/with/with_only.test new file mode 100644 index 000000000..6247617e3 --- /dev/null +++ b/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/with/with_only.test @@ -0,0 +1,10 @@ +--TEST-- +"with" tag with expression and only +--TEMPLATE-- +{% with {foo: 'foo', bar: 'BAZ'} only %} + {{ foo }}{{ bar }}{{ baz }} +{% endwith %} +--DATA-- +return array('foo' => 'baz', 'baz' => 'baz') +--EXCEPTION-- +Twig_Error_Runtime: Variable "baz" does not exist in "index.twig" at line 3. diff --git a/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/tests/defined.test b/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/tests/defined.test index cbfe03de5..d4e204efa 100644 --- a/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/tests/defined.test +++ b/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/tests/defined.test @@ -26,6 +26,13 @@ {{ object.self.foo is defined ? 'ok' : 'ko' }} {{ object.self.undefinedMethod is defined ? 'ko' : 'ok' }} {{ object.undefinedMethod.self is defined ? 'ko' : 'ok' }} +{{ 0 is defined ? 'ok' : 'ko' }} +{{ "foo" is defined ? 'ok' : 'ko' }} +{{ true is defined ? 'ok' : 'ko' }} +{{ false is defined ? 'ok' : 'ko' }} +{{ null is defined ? 'ok' : 'ko' }} +{{ [1, 2] is defined ? 'ok' : 'ko' }} +{{ { foo: "bar" } is defined ? 'ok' : 'ko' }} --DATA-- return array( 'definedVar' => 'defined', @@ -65,6 +72,13 @@ ok ok ok ok +ok +ok +ok +ok +ok +ok +ok --DATA-- return array( 'definedVar' => 'defined', @@ -106,3 +120,10 @@ ok ok ok ok +ok +ok +ok +ok +ok +ok +ok diff --git a/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/tests/defined_for_attribute.test b/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/tests/defined_for_attribute.test new file mode 100644 index 000000000..4a5b8dcdb --- /dev/null +++ b/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/tests/defined_for_attribute.test @@ -0,0 +1,35 @@ +--TEST-- +"defined" support for attribute +--TEMPLATE-- +{{ attribute(nested, "definedVar") is defined ? 'ok' : 'ko' }} +{{ attribute(nested, "undefinedVar") is not defined ? 'ok' : 'ko' }} +{{ attribute(nested, definedVarName) is defined ? 'ok' : 'ko' }} +{{ attribute(nested, undefinedVarName) is not defined ? 'ok' : 'ko' }} +--DATA-- +return array( + 'nested' => array( + 'definedVar' => 'defined', + ), + 'definedVarName' => 'definedVar', + 'undefinedVarName' => 'undefinedVar', +); +--EXPECT-- +ok +ok +ok +ok +--DATA-- +return array( + 'nested' => array( + 'definedVar' => 'defined', + ), + 'definedVarName' => 'definedVar', + 'undefinedVarName' => 'undefinedVar', +); +--CONFIG-- +return array('strict_variables' => false) +--EXPECT-- +ok +ok +ok +ok diff --git a/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/tests/defined_for_blocks.test b/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/tests/defined_for_blocks.test new file mode 100644 index 000000000..64d7d0431 --- /dev/null +++ b/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/tests/defined_for_blocks.test @@ -0,0 +1,38 @@ +--TEST-- +"defined" support for blocks +--TEMPLATE-- +{% extends 'parent' %} +{% block icon %}icon{% endblock %} +{% block body %} + {{ parent() }} + {{ block('foo') is defined ? 'ok' : 'ko' }} + {{ block('footer') is defined ? 'ok' : 'ko' }} + {{ block('icon') is defined ? 'ok' : 'ko' }} + {{ block('block1') is defined ? 'ok' : 'ko' }} + {%- embed 'embed' %} + {% block content %}content{% endblock %} + {% endembed %} +{% endblock %} +{% use 'blocks' %} +--TEMPLATE(parent)-- +{% block body %} + {{ block('icon') is defined ? 'ok' : 'ko' -}} +{% endblock %} +{% block footer %}{% endblock %} +--TEMPLATE(embed)-- +{{ block('icon') is defined ? 'ok' : 'ko' }} +{{ block('content') is defined ? 'ok' : 'ko' }} +{{ block('block1') is defined ? 'ok' : 'ko' }} +--TEMPLATE(blocks)-- +{% block block1 %}{%endblock %} +--DATA-- +return array() +--EXPECT-- +ok + ko + ok + ok + ok +ko +ok +ko diff --git a/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/tests/defined_for_blocks_with_template.test b/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/tests/defined_for_blocks_with_template.test new file mode 100644 index 000000000..2c651657e --- /dev/null +++ b/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/tests/defined_for_blocks_with_template.test @@ -0,0 +1,17 @@ +--TEST-- +"defined" support for blocks with a template argument +--TEMPLATE-- +{{ block('foo', 'included.twig') is defined ? 'ok' : 'ko' }} +{{ block('foo', included_loaded) is defined ? 'ok' : 'ko' }} +{{ block('foo', included_loaded_internal) is defined ? 'ok' : 'ko' }} +--TEMPLATE(included.twig)-- +{% block foo %}FOO{% endblock %} +--DATA-- +return array( + 'included_loaded' => $twig->load('included.twig'), + 'included_loaded_internal' => $twig->loadTemplate('included.twig'), +) +--EXPECT-- +ok +ok +ok diff --git a/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/tests/defined_for_constants.test b/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/tests/defined_for_constants.test new file mode 100644 index 000000000..2fa9929d3 --- /dev/null +++ b/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/tests/defined_for_constants.test @@ -0,0 +1,14 @@ +--TEST-- +"defined" support for constants +--TEMPLATE-- +{{ constant('DATE_W3C') is defined ? 'ok' : 'ko' }} +{{ constant('ARRAY_AS_PROPS', object) is defined ? 'ok' : 'ko' }} +{{ constant('FOOBAR') is not defined ? 'ok' : 'ko' }} +{{ constant('FOOBAR', object) is not defined ? 'ok' : 'ko' }} +--DATA-- +return array('expect' => DATE_W3C, 'object' => new ArrayObject(array('hi'))); +--EXPECT-- +ok +ok +ok +ok diff --git a/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/tests/empty.test b/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/tests/empty.test index a776d032b..807c0ed4a 100644 --- a/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/tests/empty.test +++ b/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/tests/empty.test @@ -1,35 +1,28 @@ --TEST-- "empty" test --TEMPLATE-- -{{ foo is empty ? 'ok' : 'ko' }} -{{ bar is empty ? 'ok' : 'ko' }} -{{ foobar is empty ? 'ok' : 'ko' }} -{{ array is empty ? 'ok' : 'ko' }} -{{ zero is empty ? 'ok' : 'ko' }} -{{ string is empty ? 'ok' : 'ko' }} +{{ string_empty is empty ? 'ok' : 'ko' }} +{{ string_zero is empty ? 'ko' : 'ok' }} +{{ value_null is empty ? 'ok' : 'ko' }} +{{ value_false is empty ? 'ok' : 'ko' }} +{{ value_int_zero is empty ? 'ko' : 'ok' }} +{{ array_empty is empty ? 'ok' : 'ko' }} +{{ array_not_empty is empty ? 'ko' : 'ok' }} +{{ magically_callable is empty ? 'ko' : 'ok' }} {{ countable_empty is empty ? 'ok' : 'ko' }} -{{ countable_not_empty is empty ? 'ok' : 'ko' }} +{{ countable_not_empty is empty ? 'ko' : 'ok' }} +{{ tostring_empty is empty ? 'ok' : 'ko' }} +{{ tostring_not_empty is empty ? 'ko' : 'ok' }} {{ markup_empty is empty ? 'ok' : 'ko' }} -{{ markup_not_empty is empty ? 'ok' : 'ko' }} +{{ markup_not_empty is empty ? 'ko' : 'ok' }} --DATA-- - -class CountableStub implements Countable -{ - private $items; - - public function __construct(array $items) - { - $this->items = $items; - } - - public function count() - { - return count($this->items); - } -} return array( - 'foo' => '', 'bar' => null, 'foobar' => false, 'array' => array(), 'zero' => 0, 'string' => '0', + 'string_empty' => '', 'string_zero' => '0', + 'value_null' => null, 'value_false' => false, 'value_int_zero' => 0, + 'array_empty' => array(), 'array_not_empty' => array(1, 2), + 'magically_callable' => new MagicCallStub(), 'countable_empty' => new CountableStub(array()), 'countable_not_empty' => new CountableStub(array(1, 2)), + 'tostring_empty' => new ToStringStub(''), 'tostring_not_empty' => new ToStringStub('0' /* edge case of using "0" as the string */), 'markup_empty' => new Twig_Markup('', 'UTF-8'), 'markup_not_empty' => new Twig_Markup('test', 'UTF-8'), ); --EXPECT-- @@ -37,9 +30,13 @@ ok ok ok ok -ko -ko ok -ko ok -ko +ok +ok +ok +ok +ok +ok +ok +ok diff --git a/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/tests/null_coalesce.test b/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/tests/null_coalesce.test new file mode 100644 index 000000000..3d148c893 --- /dev/null +++ b/lib/silex/vendor/twig/twig/test/Twig/Tests/Fixtures/tests/null_coalesce.test @@ -0,0 +1,30 @@ +--TEST-- +Twig supports the ?? operator +--TEMPLATE-- +{{ 'OK' ?? 'KO' }} +{{ null ?? 'OK' }} +{{ bar ?? 'KO' }} +{{ baz ?? 'OK' }} +{{ foo.bar ?? 'KO' }} +{{ foo.missing ?? 'OK' }} +{{ foo.bar.baz.missing ?? 'OK' }} +{{ foo['bar'] ?? 'KO' }} +{{ foo['missing'] ?? 'OK' }} +{{ nope ?? nada ?? 'OK' }} +{{ 1 + nope ?? nada ?? 2 }} +{{ 1 + nope ?? 3 + nada ?? 2 }} +--DATA-- +return array('bar' => 'OK', 'foo' => array('bar' => 'OK')) +--EXPECT-- +OK +OK +OK +OK +OK +OK +OK +OK +OK +OK +3 +6 diff --git a/lib/silex/vendor/twig/twig/test/Twig/Tests/IntegrationTest.php b/lib/silex/vendor/twig/twig/test/Twig/Tests/IntegrationTest.php index 1908bcdf2..5d20112de 100644 --- a/lib/silex/vendor/twig/twig/test/Twig/Tests/IntegrationTest.php +++ b/lib/silex/vendor/twig/twig/test/Twig/Tests/IntegrationTest.php @@ -141,6 +141,11 @@ class TwigTestExtension extends Twig_Extension new Twig_SimpleFilter('nl2br', array($this, 'nl2br'), array('pre_escape' => 'html', 'is_safe' => array('html'))), new Twig_SimpleFilter('escape_something', array($this, 'escape_something'), array('is_safe' => array('something'))), new Twig_SimpleFilter('preserves_safety', array($this, 'preserves_safety'), array('preserves_safety' => array('html'))), + new Twig_SimpleFilter('static_call_string', 'TwigTestExtension::staticCall'), + new Twig_SimpleFilter('static_call_array', array('TwigTestExtension', 'staticCall')), + new Twig_SimpleFilter('magic_call', array($this, 'magicCall')), + new Twig_SimpleFilter('magic_call_string', 'TwigTestExtension::magicStaticCall'), + new Twig_SimpleFilter('magic_call_array', array('TwigTestExtension', 'magicStaticCall')), new Twig_SimpleFilter('*_path', array($this, 'dynamic_path')), new Twig_SimpleFilter('*_foo_*_bar', array($this, 'dynamic_foo')), ); @@ -152,6 +157,8 @@ class TwigTestExtension extends Twig_Extension new Twig_SimpleFunction('§', array($this, '§Function')), new Twig_SimpleFunction('safe_br', array($this, 'br'), array('is_safe' => array('html'))), new Twig_SimpleFunction('unsafe_br', array($this, 'br')), + new Twig_SimpleFunction('static_call_string', 'TwigTestExtension::staticCall'), + new Twig_SimpleFunction('static_call_array', array('TwigTestExtension', 'staticCall')), new Twig_SimpleFunction('*_path', array($this, 'dynamic_path')), new Twig_SimpleFunction('*_foo_*_bar', array($this, 'dynamic_foo')), ); @@ -212,6 +219,11 @@ class TwigTestExtension extends Twig_Extension return strtoupper($value); } + public static function staticCall($value) + { + return "*$value*"; + } + public function br() { return '
    '; @@ -222,8 +234,76 @@ class TwigTestExtension extends Twig_Extension return false !== strpos($value, ' '); } - public function getName() + public function __call($method, $arguments) { - return 'integration_test'; + if ('magicCall' !== $method) { + throw new BadMethodCallException('Unexpected call to __call'); + } + + return 'magic_'.$arguments[0]; + } + + public static function __callStatic($method, $arguments) + { + if ('magicStaticCall' !== $method) { + throw new BadMethodCallException('Unexpected call to __callStatic'); + } + + return 'static_magic_'.$arguments[0]; + } +} + +/** + * This class is used in tests for the "length" filter and "empty" test. It asserts that __call is not + * used to convert such objects to strings. + */ +class MagicCallStub +{ + public function __call($name, $args) + { + throw new Exception('__call shall not be called'); + } +} + +class ToStringStub +{ + /** + * @var string + */ + private $string; + + public function __construct($string) + { + $this->string = $string; + } + + public function __toString() + { + return $this->string; + } +} + +/** + * This class is used in tests for the length filter and empty test to show + * that when \Countable is implemented, it is preferred over the __toString() + * method. + */ +class CountableStub implements \Countable +{ + private $count; + + public function __construct($count) + { + $this->count = $count; + } + + public function count() + { + return $this->count; + } + + public function __toString() + { + throw new Exception('__toString shall not be called on \Countables'); } } diff --git a/lib/silex/vendor/twig/twig/test/Twig/Tests/LegacyFixtures/autoescape/filename.legacy.test b/lib/silex/vendor/twig/twig/test/Twig/Tests/LegacyFixtures/autoescape/filename.legacy.test new file mode 100644 index 000000000..b091ad34d --- /dev/null +++ b/lib/silex/vendor/twig/twig/test/Twig/Tests/LegacyFixtures/autoescape/filename.legacy.test @@ -0,0 +1,18 @@ +--TEST-- +"filename" autoescape strategy +--TEMPLATE-- +{{ br -}} +{{ include('index.html.twig') -}} +{{ include('index.txt.twig') -}} +--TEMPLATE(index.html.twig)-- +{{ br -}} +--TEMPLATE(index.txt.twig)-- +{{ br -}} +--DATA-- +return array('br' => '
    ') +--CONFIG-- +return array('autoescape' => 'filename') +--EXPECT-- +<br /> +<br /> +
    diff --git a/lib/silex/vendor/twig/twig/test/Twig/Tests/LegacyFixtures/functions/undefined_block.legacy.test b/lib/silex/vendor/twig/twig/test/Twig/Tests/LegacyFixtures/functions/undefined_block.legacy.test new file mode 100644 index 000000000..62e24f0f4 --- /dev/null +++ b/lib/silex/vendor/twig/twig/test/Twig/Tests/LegacyFixtures/functions/undefined_block.legacy.test @@ -0,0 +1,12 @@ +--TEST-- +"block" function with undefined block +--TEMPLATE-- +{% extends "base.twig" %} +{% block foo %}{{ parent() }}{{ block('unknown') }}{{ block('bar') }}{% endblock %} +--TEMPLATE(base.twig)-- +{% block foo %}Foo{% endblock %} +{% block bar %}Bar{% endblock %} +--DATA-- +return array() +--EXPECT-- +FooBarBar diff --git a/lib/silex/vendor/twig/twig/test/Twig/Tests/LegacyIntegrationTest.php b/lib/silex/vendor/twig/twig/test/Twig/Tests/LegacyIntegrationTest.php index 02ec3cb29..2ed758038 100644 --- a/lib/silex/vendor/twig/twig/test/Twig/Tests/LegacyIntegrationTest.php +++ b/lib/silex/vendor/twig/twig/test/Twig/Tests/LegacyIntegrationTest.php @@ -22,6 +22,15 @@ class Twig_Tests_LegacyIntegrationTest extends Twig_Test_IntegrationTestCase { return dirname(__FILE__).'/LegacyFixtures/'; } + + public function getTests($name, $legacyTests = false) + { + if (!$legacyTests) { + return array(array('', '', '', array(), '', array())); + } + + return parent::getTests($name, true); + } } class LegacyTwigTestExtension extends Twig_Extension diff --git a/lib/silex/vendor/twig/twig/test/Twig/Tests/LexerTest.php b/lib/silex/vendor/twig/twig/test/Twig/Tests/LexerTest.php index 4945d2247..ffe9a235b 100644 --- a/lib/silex/vendor/twig/twig/test/Twig/Tests/LexerTest.php +++ b/lib/silex/vendor/twig/twig/test/Twig/Tests/LexerTest.php @@ -8,14 +8,25 @@ * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -class Twig_Tests_LexerTest extends PHPUnit_Framework_TestCase +class Twig_Tests_LexerTest extends \PHPUnit\Framework\TestCase { + /** + * @group legacy + */ + public function testLegacyConstructorSignature() + { + $lexer = new Twig_Lexer(new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock())); + $stream = $lexer->tokenize('{{ foo }}', 'foo'); + $this->assertEquals('foo', $stream->getFilename()); + $this->assertEquals('{{ foo }}', $stream->getSource()); + } + public function testNameLabelForTag() { $template = '{% § %}'; - $lexer = new Twig_Lexer(new Twig_Environment($this->getMock('Twig_LoaderInterface'))); - $stream = $lexer->tokenize($template); + $lexer = new Twig_Lexer(new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock())); + $stream = $lexer->tokenize(new Twig_Source($template, 'index')); $stream->expect(Twig_Token::BLOCK_START_TYPE); $this->assertSame('§', $stream->expect(Twig_Token::NAME_TYPE)->getValue()); @@ -25,8 +36,8 @@ class Twig_Tests_LexerTest extends PHPUnit_Framework_TestCase { $template = '{{ §() }}'; - $lexer = new Twig_Lexer(new Twig_Environment($this->getMock('Twig_LoaderInterface'))); - $stream = $lexer->tokenize($template); + $lexer = new Twig_Lexer(new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock())); + $stream = $lexer->tokenize(new Twig_Source($template, 'index')); $stream->expect(Twig_Token::VAR_START_TYPE); $this->assertSame('§', $stream->expect(Twig_Token::NAME_TYPE)->getValue()); @@ -42,8 +53,8 @@ class Twig_Tests_LexerTest extends PHPUnit_Framework_TestCase protected function countToken($template, $type, $value = null) { - $lexer = new Twig_Lexer(new Twig_Environment($this->getMock('Twig_LoaderInterface'))); - $stream = $lexer->tokenize($template); + $lexer = new Twig_Lexer(new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock())); + $stream = $lexer->tokenize(new Twig_Source($template, 'index')); $count = 0; while (!$stream->isEOF()) { @@ -67,8 +78,8 @@ class Twig_Tests_LexerTest extends PHPUnit_Framework_TestCase ."baz\n" ."}}\n"; - $lexer = new Twig_Lexer(new Twig_Environment($this->getMock('Twig_LoaderInterface'))); - $stream = $lexer->tokenize($template); + $lexer = new Twig_Lexer(new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock())); + $stream = $lexer->tokenize(new Twig_Source($template, 'index')); // foo\nbar\n $this->assertSame(1, $stream->expect(Twig_Token::TEXT_TYPE)->getLine()); @@ -87,8 +98,8 @@ class Twig_Tests_LexerTest extends PHPUnit_Framework_TestCase ."baz\n" ."}}\n"; - $lexer = new Twig_Lexer(new Twig_Environment($this->getMock('Twig_LoaderInterface'))); - $stream = $lexer->tokenize($template); + $lexer = new Twig_Lexer(new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock())); + $stream = $lexer->tokenize(new Twig_Source($template, 'index')); // foo\nbar $this->assertSame(1, $stream->expect(Twig_Token::TEXT_TYPE)->getLine()); @@ -102,48 +113,56 @@ class Twig_Tests_LexerTest extends PHPUnit_Framework_TestCase { $template = '{# '.str_repeat('*', 100000).' #}'; - $lexer = new Twig_Lexer(new Twig_Environment($this->getMock('Twig_LoaderInterface'))); - $lexer->tokenize($template); + $lexer = new Twig_Lexer(new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock())); + $lexer->tokenize(new Twig_Source($template, 'index')); - // should not throw an exception + // add a dummy assertion here to satisfy PHPUnit, the only thing we want to test is that the code above + // can be executed without throwing any exceptions + $this->addToAssertionCount(1); } public function testLongVerbatim() { $template = '{% verbatim %}'.str_repeat('*', 100000).'{% endverbatim %}'; - $lexer = new Twig_Lexer(new Twig_Environment($this->getMock('Twig_LoaderInterface'))); - $lexer->tokenize($template); + $lexer = new Twig_Lexer(new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock())); + $lexer->tokenize(new Twig_Source($template, 'index')); - // should not throw an exception + // add a dummy assertion here to satisfy PHPUnit, the only thing we want to test is that the code above + // can be executed without throwing any exceptions + $this->addToAssertionCount(1); } public function testLongVar() { $template = '{{ '.str_repeat('x', 100000).' }}'; - $lexer = new Twig_Lexer(new Twig_Environment($this->getMock('Twig_LoaderInterface'))); - $lexer->tokenize($template); + $lexer = new Twig_Lexer(new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock())); + $lexer->tokenize(new Twig_Source($template, 'index')); - // should not throw an exception + // add a dummy assertion here to satisfy PHPUnit, the only thing we want to test is that the code above + // can be executed without throwing any exceptions + $this->addToAssertionCount(1); } public function testLongBlock() { $template = '{% '.str_repeat('x', 100000).' %}'; - $lexer = new Twig_Lexer(new Twig_Environment($this->getMock('Twig_LoaderInterface'))); - $lexer->tokenize($template); + $lexer = new Twig_Lexer(new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock())); + $lexer->tokenize(new Twig_Source($template, 'index')); - // should not throw an exception + // add a dummy assertion here to satisfy PHPUnit, the only thing we want to test is that the code above + // can be executed without throwing any exceptions + $this->addToAssertionCount(1); } public function testBigNumbers() { $template = '{{ 922337203685477580700 }}'; - $lexer = new Twig_Lexer(new Twig_Environment($this->getMock('Twig_LoaderInterface'))); - $stream = $lexer->tokenize($template); + $lexer = new Twig_Lexer(new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock())); + $stream = $lexer->tokenize(new Twig_Source($template, 'index')); $stream->next(); $node = $stream->next(); $this->assertEquals('922337203685477580700', $node->getValue()); @@ -155,11 +174,15 @@ class Twig_Tests_LexerTest extends PHPUnit_Framework_TestCase "{{ 'foo \' bar' }}" => 'foo \' bar', '{{ "foo \" bar" }}' => 'foo " bar', ); - $lexer = new Twig_Lexer(new Twig_Environment($this->getMock('Twig_LoaderInterface'))); + $lexer = new Twig_Lexer(new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock())); foreach ($tests as $template => $expected) { - $stream = $lexer->tokenize($template); + $stream = $lexer->tokenize(new Twig_Source($template, 'index')); $stream->expect(Twig_Token::VAR_START_TYPE); $stream->expect(Twig_Token::STRING_TYPE, $expected); + + // add a dummy assertion here to satisfy PHPUnit, the only thing we want to test is that the code above + // can be executed without throwing any exceptions + $this->addToAssertionCount(1); } } @@ -167,8 +190,8 @@ class Twig_Tests_LexerTest extends PHPUnit_Framework_TestCase { $template = 'foo {{ "bar #{ baz + 1 }" }}'; - $lexer = new Twig_Lexer(new Twig_Environment($this->getMock('Twig_LoaderInterface'))); - $stream = $lexer->tokenize($template); + $lexer = new Twig_Lexer(new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock())); + $stream = $lexer->tokenize(new Twig_Source($template, 'index')); $stream->expect(Twig_Token::TEXT_TYPE, 'foo '); $stream->expect(Twig_Token::VAR_START_TYPE); $stream->expect(Twig_Token::STRING_TYPE, 'bar '); @@ -178,28 +201,40 @@ class Twig_Tests_LexerTest extends PHPUnit_Framework_TestCase $stream->expect(Twig_Token::NUMBER_TYPE, '1'); $stream->expect(Twig_Token::INTERPOLATION_END_TYPE); $stream->expect(Twig_Token::VAR_END_TYPE); + + // add a dummy assertion here to satisfy PHPUnit, the only thing we want to test is that the code above + // can be executed without throwing any exceptions + $this->addToAssertionCount(1); } public function testStringWithEscapedInterpolation() { $template = '{{ "bar \#{baz+1}" }}'; - $lexer = new Twig_Lexer(new Twig_Environment($this->getMock('Twig_LoaderInterface'))); - $stream = $lexer->tokenize($template); + $lexer = new Twig_Lexer(new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock())); + $stream = $lexer->tokenize(new Twig_Source($template, 'index')); $stream->expect(Twig_Token::VAR_START_TYPE); $stream->expect(Twig_Token::STRING_TYPE, 'bar #{baz+1}'); $stream->expect(Twig_Token::VAR_END_TYPE); + + // add a dummy assertion here to satisfy PHPUnit, the only thing we want to test is that the code above + // can be executed without throwing any exceptions + $this->addToAssertionCount(1); } public function testStringWithHash() { $template = '{{ "bar # baz" }}'; - $lexer = new Twig_Lexer(new Twig_Environment($this->getMock('Twig_LoaderInterface'))); - $stream = $lexer->tokenize($template); + $lexer = new Twig_Lexer(new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock())); + $stream = $lexer->tokenize(new Twig_Source($template, 'index')); $stream->expect(Twig_Token::VAR_START_TYPE); $stream->expect(Twig_Token::STRING_TYPE, 'bar # baz'); $stream->expect(Twig_Token::VAR_END_TYPE); + + // add a dummy assertion here to satisfy PHPUnit, the only thing we want to test is that the code above + // can be executed without throwing any exceptions + $this->addToAssertionCount(1); } /** @@ -210,16 +245,16 @@ class Twig_Tests_LexerTest extends PHPUnit_Framework_TestCase { $template = '{{ "bar #{x" }}'; - $lexer = new Twig_Lexer(new Twig_Environment($this->getMock('Twig_LoaderInterface'))); - $lexer->tokenize($template); + $lexer = new Twig_Lexer(new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock())); + $lexer->tokenize(new Twig_Source($template, 'index')); } public function testStringWithNestedInterpolations() { $template = '{{ "bar #{ "foo#{bar}" }" }}'; - $lexer = new Twig_Lexer(new Twig_Environment($this->getMock('Twig_LoaderInterface'))); - $stream = $lexer->tokenize($template); + $lexer = new Twig_Lexer(new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock())); + $stream = $lexer->tokenize(new Twig_Source($template, 'index')); $stream->expect(Twig_Token::VAR_START_TYPE); $stream->expect(Twig_Token::STRING_TYPE, 'bar '); $stream->expect(Twig_Token::INTERPOLATION_START_TYPE); @@ -229,14 +264,18 @@ class Twig_Tests_LexerTest extends PHPUnit_Framework_TestCase $stream->expect(Twig_Token::INTERPOLATION_END_TYPE); $stream->expect(Twig_Token::INTERPOLATION_END_TYPE); $stream->expect(Twig_Token::VAR_END_TYPE); + + // add a dummy assertion here to satisfy PHPUnit, the only thing we want to test is that the code above + // can be executed without throwing any exceptions + $this->addToAssertionCount(1); } public function testStringWithNestedInterpolationsInBlock() { $template = '{% foo "bar #{ "foo#{bar}" }" %}'; - $lexer = new Twig_Lexer(new Twig_Environment($this->getMock('Twig_LoaderInterface'))); - $stream = $lexer->tokenize($template); + $lexer = new Twig_Lexer(new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock())); + $stream = $lexer->tokenize(new Twig_Source($template, 'index')); $stream->expect(Twig_Token::BLOCK_START_TYPE); $stream->expect(Twig_Token::NAME_TYPE, 'foo'); $stream->expect(Twig_Token::STRING_TYPE, 'bar '); @@ -247,22 +286,30 @@ class Twig_Tests_LexerTest extends PHPUnit_Framework_TestCase $stream->expect(Twig_Token::INTERPOLATION_END_TYPE); $stream->expect(Twig_Token::INTERPOLATION_END_TYPE); $stream->expect(Twig_Token::BLOCK_END_TYPE); + + // add a dummy assertion here to satisfy PHPUnit, the only thing we want to test is that the code above + // can be executed without throwing any exceptions + $this->addToAssertionCount(1); } public function testOperatorEndingWithALetterAtTheEndOfALine() { $template = "{{ 1 and\n0}}"; - $lexer = new Twig_Lexer(new Twig_Environment($this->getMock('Twig_LoaderInterface'))); - $stream = $lexer->tokenize($template); + $lexer = new Twig_Lexer(new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock())); + $stream = $lexer->tokenize(new Twig_Source($template, 'index')); $stream->expect(Twig_Token::VAR_START_TYPE); $stream->expect(Twig_Token::NUMBER_TYPE, 1); $stream->expect(Twig_Token::OPERATOR_TYPE, 'and'); + + // add a dummy assertion here to satisfy PHPUnit, the only thing we want to test is that the code above + // can be executed without throwing any exceptions + $this->addToAssertionCount(1); } /** * @expectedException Twig_Error_Syntax - * @expectedExceptionMessage Unclosed "variable" at line 3 + * @expectedExceptionMessage Unclosed "variable" in "index" at line 3 */ public function testUnterminatedVariable() { @@ -275,13 +322,13 @@ bar '; - $lexer = new Twig_Lexer(new Twig_Environment($this->getMock('Twig_LoaderInterface'))); - $lexer->tokenize($template); + $lexer = new Twig_Lexer(new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock())); + $lexer->tokenize(new Twig_Source($template, 'index')); } /** * @expectedException Twig_Error_Syntax - * @expectedExceptionMessage Unclosed "block" at line 3 + * @expectedExceptionMessage Unclosed "block" in "index" at line 3 */ public function testUnterminatedBlock() { @@ -294,7 +341,7 @@ bar '; - $lexer = new Twig_Lexer(new Twig_Environment($this->getMock('Twig_LoaderInterface'))); - $lexer->tokenize($template); + $lexer = new Twig_Lexer(new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock())); + $lexer->tokenize(new Twig_Source($template, 'index')); } } diff --git a/lib/silex/vendor/twig/twig/test/Twig/Tests/Loader/ArrayTest.php b/lib/silex/vendor/twig/twig/test/Twig/Tests/Loader/ArrayTest.php index 1369a6bd0..593063933 100644 --- a/lib/silex/vendor/twig/twig/test/Twig/Tests/Loader/ArrayTest.php +++ b/lib/silex/vendor/twig/twig/test/Twig/Tests/Loader/ArrayTest.php @@ -9,8 +9,11 @@ * file that was distributed with this source code. */ -class Twig_Tests_Loader_ArrayTest extends PHPUnit_Framework_TestCase +class Twig_Tests_Loader_ArrayTest extends \PHPUnit\Framework\TestCase { + /** + * @group legacy + */ public function testGetSource() { $loader = new Twig_Loader_Array(array('foo' => 'bar')); @@ -19,6 +22,7 @@ class Twig_Tests_Loader_ArrayTest extends PHPUnit_Framework_TestCase } /** + * @group legacy * @expectedException Twig_Error_Loader */ public function testGetSourceWhenTemplateDoesNotExist() @@ -28,11 +32,43 @@ class Twig_Tests_Loader_ArrayTest extends PHPUnit_Framework_TestCase $loader->getSource('foo'); } + /** + * @expectedException Twig_Error_Loader + */ + public function testGetSourceContextWhenTemplateDoesNotExist() + { + $loader = new Twig_Loader_Array(array()); + + $loader->getSourceContext('foo'); + } + public function testGetCacheKey() { $loader = new Twig_Loader_Array(array('foo' => 'bar')); - $this->assertEquals('bar', $loader->getCacheKey('foo')); + $this->assertEquals('foo:bar', $loader->getCacheKey('foo')); + } + + public function testGetCacheKeyWhenTemplateHasDuplicateContent() + { + $loader = new Twig_Loader_Array(array( + 'foo' => 'bar', + 'baz' => 'bar', + )); + + $this->assertEquals('foo:bar', $loader->getCacheKey('foo')); + $this->assertEquals('baz:bar', $loader->getCacheKey('baz')); + } + + public function testGetCacheKeyIsProtectedFromEdgeCollisions() + { + $loader = new Twig_Loader_Array(array( + 'foo__' => 'bar', + 'foo' => '__bar', + )); + + $this->assertEquals('foo__:bar', $loader->getCacheKey('foo__')); + $this->assertEquals('foo:__bar', $loader->getCacheKey('foo')); } /** @@ -50,7 +86,7 @@ class Twig_Tests_Loader_ArrayTest extends PHPUnit_Framework_TestCase $loader = new Twig_Loader_Array(array()); $loader->setTemplate('foo', 'bar'); - $this->assertEquals('bar', $loader->getSource('foo')); + $this->assertEquals('bar', $loader->getSourceContext('foo')->getCode()); } public function testIsFresh() @@ -75,9 +111,13 @@ class Twig_Tests_Loader_ArrayTest extends PHPUnit_Framework_TestCase $loader = new Twig_Loader_Array(array('foo' => 'bar')); $loader->getCacheKey($name); - $loader->getSource($name); + $loader->getSourceContext($name); $loader->isFresh($name, time()); - $loader->setTemplate($name, 'foobar'); + $loader->setTemplate($name, 'foo:bar'); + + // add a dummy assertion here to satisfy PHPUnit, the only thing we want to test is that the code above + // can be executed without crashing PHP + $this->addToAssertionCount(1); } } diff --git a/lib/silex/vendor/twig/twig/test/Twig/Tests/Loader/ChainTest.php b/lib/silex/vendor/twig/twig/test/Twig/Tests/Loader/ChainTest.php index 4fe0db948..8a8a84a5a 100644 --- a/lib/silex/vendor/twig/twig/test/Twig/Tests/Loader/ChainTest.php +++ b/lib/silex/vendor/twig/twig/test/Twig/Tests/Loader/ChainTest.php @@ -9,8 +9,11 @@ * file that was distributed with this source code. */ -class Twig_Tests_Loader_ChainTest extends PHPUnit_Framework_TestCase +class Twig_Tests_Loader_ChainTest extends \PHPUnit\Framework\TestCase { + /** + * @group legacy + */ public function testGetSource() { $loader = new Twig_Loader_Chain(array( @@ -22,9 +25,41 @@ class Twig_Tests_Loader_ChainTest extends PHPUnit_Framework_TestCase $this->assertEquals('foo', $loader->getSource('bar')); } + public function testGetSourceContext() + { + $path = dirname(__FILE__).'/../Fixtures'; + $loader = new Twig_Loader_Chain(array( + new Twig_Loader_Array(array('foo' => 'bar')), + new Twig_Loader_Array(array('errors/index.html' => 'baz')), + new Twig_Loader_Filesystem(array($path)), + )); + + $this->assertEquals('foo', $loader->getSourceContext('foo')->getName()); + $this->assertSame('', $loader->getSourceContext('foo')->getPath()); + + $this->assertEquals('errors/index.html', $loader->getSourceContext('errors/index.html')->getName()); + $this->assertSame('', $loader->getSourceContext('errors/index.html')->getPath()); + $this->assertEquals('baz', $loader->getSourceContext('errors/index.html')->getCode()); + + $this->assertEquals('errors/base.html', $loader->getSourceContext('errors/base.html')->getName()); + $this->assertEquals(realpath($path.'/errors/base.html'), realpath($loader->getSourceContext('errors/base.html')->getPath())); + $this->assertNotEquals('baz', $loader->getSourceContext('errors/base.html')->getCode()); + } + /** * @expectedException Twig_Error_Loader */ + public function testGetSourceContextWhenTemplateDoesNotExist() + { + $loader = new Twig_Loader_Chain(array()); + + $loader->getSourceContext('foo'); + } + + /** + * @group legacy + * @expectedException Twig_Error_Loader + */ public function testGetSourceWhenTemplateDoesNotExist() { $loader = new Twig_Loader_Chain(array()); @@ -39,8 +74,8 @@ class Twig_Tests_Loader_ChainTest extends PHPUnit_Framework_TestCase new Twig_Loader_Array(array('foo' => 'foobar', 'bar' => 'foo')), )); - $this->assertEquals('bar', $loader->getCacheKey('foo')); - $this->assertEquals('foo', $loader->getCacheKey('bar')); + $this->assertEquals('foo:bar', $loader->getCacheKey('foo')); + $this->assertEquals('bar:foo', $loader->getCacheKey('bar')); } /** @@ -58,17 +93,19 @@ class Twig_Tests_Loader_ChainTest extends PHPUnit_Framework_TestCase $loader = new Twig_Loader_Chain(); $loader->addLoader(new Twig_Loader_Array(array('foo' => 'bar'))); - $this->assertEquals('bar', $loader->getSource('foo')); + $this->assertEquals('bar', $loader->getSourceContext('foo')->getCode()); } public function testExists() { - $loader1 = $this->getMock('Twig_Loader_Array', array('exists', 'getSource'), array(), '', false); + $loader1 = $this->getMockBuilder('Twig_ChainTestLoaderWithExistsInterface')->getMock(); $loader1->expects($this->once())->method('exists')->will($this->returnValue(false)); - $loader1->expects($this->never())->method('getSource'); + $loader1->expects($this->never())->method('getSourceContext'); - $loader2 = $this->getMock('Twig_LoaderInterface'); - $loader2->expects($this->once())->method('getSource')->will($this->returnValue('content')); + // can be removed in 2.0 + $loader2 = $this->getMockBuilder('Twig_ChainTestLoaderInterface')->getMock(); + //$loader2 = $this->getMockBuilder(array('Twig_LoaderInterface', 'Twig_SourceContextLoaderInterface'))->getMock(); + $loader2->expects($this->once())->method('getSourceContext')->will($this->returnValue(new Twig_Source('content', 'index'))); $loader = new Twig_Loader_Chain(); $loader->addLoader($loader1); @@ -77,3 +114,11 @@ class Twig_Tests_Loader_ChainTest extends PHPUnit_Framework_TestCase $this->assertTrue($loader->exists('foo')); } } + +interface Twig_ChainTestLoaderInterface extends Twig_LoaderInterface, Twig_SourceContextLoaderInterface +{ +} + +interface Twig_ChainTestLoaderWithExistsInterface extends Twig_LoaderInterface, Twig_ExistsLoaderInterface, Twig_SourceContextLoaderInterface +{ +} diff --git a/lib/silex/vendor/twig/twig/test/Twig/Tests/Loader/FilesystemTest.php b/lib/silex/vendor/twig/twig/test/Twig/Tests/Loader/FilesystemTest.php index e07f374a3..bd2acbd75 100644 --- a/lib/silex/vendor/twig/twig/test/Twig/Tests/Loader/FilesystemTest.php +++ b/lib/silex/vendor/twig/twig/test/Twig/Tests/Loader/FilesystemTest.php @@ -9,8 +9,16 @@ * file that was distributed with this source code. */ -class Twig_Tests_Loader_FilesystemTest extends PHPUnit_Framework_TestCase +class Twig_Tests_Loader_FilesystemTest extends \PHPUnit\Framework\TestCase { + public function testGetSourceContext() + { + $path = dirname(__FILE__).'/../Fixtures'; + $loader = new Twig_Loader_Filesystem(array($path)); + $this->assertEquals('errors/index.html', $loader->getSourceContext('errors/index.html')->getName()); + $this->assertEquals(realpath($path.'/errors/index.html'), realpath($loader->getSourceContext('errors/index.html')->getPath())); + } + /** * @dataProvider getSecurityTests */ @@ -51,11 +59,12 @@ class Twig_Tests_Loader_FilesystemTest extends PHPUnit_Framework_TestCase ); } - public function testPaths() + /** + * @dataProvider getBasePaths + */ + public function testPaths($basePath, $cacheKey, $rootPath) { - $basePath = dirname(__FILE__).'/Fixtures'; - - $loader = new Twig_Loader_Filesystem(array($basePath.'/normal', $basePath.'/normal_bis')); + $loader = new Twig_Loader_Filesystem(array($basePath.'/normal', $basePath.'/normal_bis'), $rootPath); $loader->setPaths(array($basePath.'/named', $basePath.'/named_bis'), 'named'); $loader->addPath($basePath.'/named_ter', 'named'); $loader->addPath($basePath.'/normal_ter'); @@ -77,13 +86,42 @@ class Twig_Tests_Loader_FilesystemTest extends PHPUnit_Framework_TestCase $basePath.'/named_ter', ), $loader->getPaths('named')); - $this->assertEquals( - realpath($basePath.'/named_quater/named_absolute.html'), - realpath($loader->getCacheKey('@named/named_absolute.html')) + // do not use realpath here as it would make the test unuseful + $this->assertEquals($cacheKey, str_replace('\\', '/', $loader->getCacheKey('@named/named_absolute.html'))); + $this->assertEquals("path (final)\n", $loader->getSourceContext('index.html')->getCode()); + $this->assertEquals("path (final)\n", $loader->getSourceContext('@__main__/index.html')->getCode()); + $this->assertEquals("named path (final)\n", $loader->getSourceContext('@named/index.html')->getCode()); + } + + public function getBasePaths() + { + return array( + array( + dirname(__FILE__).'/Fixtures', + 'test/Twig/Tests/Loader/Fixtures/named_quater/named_absolute.html', + null, + ), + array( + dirname(__FILE__).'/Fixtures/../Fixtures', + 'test/Twig/Tests/Loader/Fixtures/named_quater/named_absolute.html', + null, + ), + array( + 'test/Twig/Tests/Loader/Fixtures', + 'test/Twig/Tests/Loader/Fixtures/named_quater/named_absolute.html', + getcwd(), + ), + array( + 'Fixtures', + 'Fixtures/named_quater/named_absolute.html', + getcwd().'/test/Twig/Tests/Loader', + ), + array( + 'Fixtures', + 'Fixtures/named_quater/named_absolute.html', + getcwd().'/test/../test/Twig/Tests/Loader', + ), ); - $this->assertEquals("path (final)\n", $loader->getSource('index.html')); - $this->assertEquals("path (final)\n", $loader->getSource('@__main__/index.html')); - $this->assertEquals("named path (final)\n", $loader->getSource('@named/index.html')); } public function testEmptyConstructor() @@ -109,7 +147,7 @@ class Twig_Tests_Loader_FilesystemTest extends PHPUnit_Framework_TestCase $loader->addPath($basePath.'/named', 'named'); try { - $loader->getSource('@named/nowhere.html'); + $loader->getSourceContext('@named/nowhere.html'); } catch (Exception $e) { $this->assertInstanceof('Twig_Error_Loader', $e); $this->assertContains('Unable to find template "@named/nowhere.html"', $e->getMessage()); @@ -124,11 +162,11 @@ class Twig_Tests_Loader_FilesystemTest extends PHPUnit_Framework_TestCase $loader->addPath($basePath.'/named', 'named'); // prime the cache for index.html in the named namespace - $namedSource = $loader->getSource('@named/index.html'); + $namedSource = $loader->getSourceContext('@named/index.html')->getCode(); $this->assertEquals("named path\n", $namedSource); // get index.html from the main namespace - $this->assertEquals("path\n", $loader->getSource('index.html')); + $this->assertEquals("path\n", $loader->getSourceContext('index.html')->getCode()); } public function testLoadTemplateAndRenderBlockWithCache() @@ -172,4 +210,17 @@ class Twig_Tests_Loader_FilesystemTest extends PHPUnit_Framework_TestCase $template = $twig->loadTemplate($templateName); $this->assertSame('VALID Child', $template->renderBlock('body', array())); } + + /** + * @requires PHP 5.3 + */ + public function testLoadTemplateFromPhar() + { + $loader = new Twig_Loader_Filesystem(array()); + // phar-sample.phar was created with the following script: + // $f = new Phar('phar-test.phar'); + // $f->addFromString('hello.twig', 'hello from phar'); + $loader->addPath('phar://'.dirname(__FILE__).'/Fixtures/phar/phar-sample.phar'); + $this->assertSame('hello from phar', $loader->getSourceContext('hello.twig')->getCode()); + } } diff --git a/lib/silex/vendor/twig/twig/test/Twig/Tests/Loader/Fixtures/phar/phar-sample.phar b/lib/silex/vendor/twig/twig/test/Twig/Tests/Loader/Fixtures/phar/phar-sample.phar new file mode 100644 index 0000000000000000000000000000000000000000..092bbfae3e4c98b308dc98bf7653ead68ae3ee6e GIT binary patch literal 6786 zcmb_hOLN=E5q5IqWh?g_#w@G>Mx-dx&rq@~*-JkvlEqM5ZLJv>0s=!4E4&r}B}(hP zu&2!Q zBabh)K{9u1HGj%1KeC-Pb*?NknLDZ3WLchaC$wigw=YvCNqCxBjRw1SkGX-9Wj0^> zS)N(uK(g;(@4Ff;dObhNdFt{cf9VJO@V}w zadTVhIUOAiN4vF@=ZiGjt$i4sBa?mjz}5||unkU5M`lALde!||Y~|^KBX%S7L!RM;DW<2Eh9F$qo(Np_{kp$|P=Z;JZ%l z$5DIP`o_6%GB@>;oK%Lsr_$prJU?!WwWLdxx5%-|y&_{t%<+X}#s{Cbzg_t5d5&Tf zd&&J3MOczVqCA#|5~={jZxP8VJ@M2LltUpRR3*B2Wbm{EHM^{k$mX$oe(7BB))akg zPJH?5;bJM2ir=M_YC43=(a;h~mJ?MA(gj))6(u25hzg-qvxaM0n{&tGsa1dBxO3im z5l49%2R#7h?{wI4e8D|7;A!YYJj#PB z^Cq?CQ7o@WkI_(CZ^t4}*pV{?wjhf^nA6BG97K+p8HnLlkv7PE@n+b6fvNWO%7Bx4 z#0fzOLQ)|k{dA(1;ov;>G_^vrI4)HlI@%rfVCMmQ8|Un0yofxClDqSm)%U3J?EfKR zdn7?0*k11E0pF)_eIucW#MsuHdz09^k|OiYJ{f2+Ayr55Bzcq7OY&{72x%n*HDtM7 zN*Q4hN|zr^V+vE9tQxA5v585=qYO<8la!8E2T3;ur`H-?CA`N<1FdwJn&*f|vwSX( zbczr&|61mh3fJ+7ByH=H6nIEZelbqR#;PaM72=ZPc5NT^_>^_X-{PJ)H&K5|uqy)f zXY&C6b9>#t>Lj3IH@Ne2API}dtQVmI=2Lw!w5ZrpxBzWz`CBja`W)LGJ*yWt=wxw+mS-@9K5XOZu$ckZ6v4b!? zpIc^sI6N8l7#gTQ?f#|I;*1 zd+a*!C#+~N%+D}>bA};02{0jA6oknNd8@&+au#v!fn^$}LP^Vw!zAVC7#_o$%1JXB zlzK*#JVn)Sc6D;?ajj|H+sU<`bU*2CS;q#CC)eGNI4mx5aSnNy5C+gSaaZnQLf!Q< zJPZAta%D+SAj&pF7I6Ehrwb)wWoLj}^34ZL)c#N$03zt0C4LN#-Fk!FMe9`8wY*E( z1gf|#v{}_wM_Ic|EmT)TS!siYErmisu51*w@0szR$>{jy>FZ3E^~zEw6%4o2Y!kPVY@6E}eU&?ts3%e=FGiu1bQDC@ZjmFrmEB8^ z4(uR27f}&olkGfy{J6>VBT+a|0xR+W)x}!Pmh^Q4a0$JKay;o)Dj%on4yPPX0X_6Ltez1B^mjZ>a#xRo3j8g)V~Wo(A#dxoZD$eP?| z2B{XsN`P%8S|m&YE)}(gd8lS;?JA`oWYb7xzf`GS(fE9BW_@U>e-u z*z)leK?^x)0`7dzIHnq!+NW}*#N@H4a-%b`)P~3i#@aLyQM&Um#zb4s0W3dy^oT5r ze>M3)SS=nkb48a^Zv<&qeZ@keYqMIn84T!~?qCgG5A2gu9P3wa-U8d-GzNq$Fw}*U z%97{Ng=LP82jF@}1y)-3Zq|KbAjcFeFd$E{kS^5&%r-?0HRucS{%A@`4&@~=zTP7x zN~h>tS}7S=brAT!(gh`5)=X}YqP1j|W+MR_H8;!nL_fbm99WYHR01RbPc|@hG?&0G zCF#xsyuXY9I0dd4^tI9k4TS<4;lOwXYCa`|A{1m$02+c616;DuJ*7J;Rv7O9Kk~wY z7oWDaDC02BBXDdmE6u17mCY>}-Gv~{96g5Aa}@c|5~Np$C0vD=qCuE3=`32uxKM=K zDZf>-@hD%Qp%qR!?XyYy%x#}}?K9p!!70eif2a zY}5~RK6da`w1npMH<9?ph>5S@BPWn}l+lC$>cYd9aRR@1r*9Vz52J-!82Nj$#F)uGaeJ3q1ba)V<%e^m|15jfyCh>Q8eK-+j5~*&>u~YU3y&d@s+0 zTH(#@$DUn{H4Fue=;e27SQRK?7Q_?F{0(|OfrP29CNWdh-#9=OWQ7yK^Nby&p@3Id z^w_~73jBzkjz|_A^DSxmtBFRLQr8BEG+n_*YRqa=G@77)9aE^#bg-&a`zwFpz;hU~ zw*C6>Xk@=QIUeB8Eklslv;8M`?%eqW{{Kq-#xMNT@PE#OAl}X|{n=+&{rQLgenr2R zUq3wj2MORuaIk3_hXVQj>yQ8J|L^Zl{`Bz2U;pj!umAb+FF!2*{Y$XDdVc);|E1Ux AZ~y=R literal 0 HcmV?d00001 diff --git a/lib/silex/vendor/twig/twig/test/Twig/Tests/NativeExtensionTest.php b/lib/silex/vendor/twig/twig/test/Twig/Tests/NativeExtensionTest.php index 942aff9e3..50736dc22 100644 --- a/lib/silex/vendor/twig/twig/test/Twig/Tests/NativeExtensionTest.php +++ b/lib/silex/vendor/twig/twig/test/Twig/Tests/NativeExtensionTest.php @@ -9,14 +9,13 @@ * file that was distributed with this source code. */ -class Twig_Tests_NativeExtensionTest extends PHPUnit_Framework_TestCase +class Twig_Tests_NativeExtensionTest extends \PHPUnit\Framework\TestCase { + /** + * @requires PHP 5.3 + */ public function testGetProperties() { - if (defined('HHVM_VERSION')) { - $this->markTestSkipped('Skip under HHVM as the behavior is not the same as plain PHP (which is an edge case anyway)'); - } - $twig = new Twig_Environment(new Twig_Loader_Array(array('index' => '{{ d1.date }}{{ d2.date }}')), array( 'debug' => true, 'cache' => false, diff --git a/lib/silex/vendor/twig/twig/test/Twig/Tests/Node/Expression/Binary/FloorDivTest.php b/lib/silex/vendor/twig/twig/test/Twig/Tests/Node/Expression/Binary/FloorDivTest.php index 602888fd5..5813dce6e 100644 --- a/lib/silex/vendor/twig/twig/test/Twig/Tests/Node/Expression/Binary/FloorDivTest.php +++ b/lib/silex/vendor/twig/twig/test/Twig/Tests/Node/Expression/Binary/FloorDivTest.php @@ -28,7 +28,7 @@ class Twig_Tests_Node_Expression_Binary_FloorDivTest extends Twig_Test_NodeTestC $node = new Twig_Node_Expression_Binary_FloorDiv($left, $right, 1); return array( - array($node, 'intval(floor((1 / 2)))'), + array($node, '(int) floor((1 / 2))'), ); } } diff --git a/lib/silex/vendor/twig/twig/test/Twig/Tests/Node/Expression/CallTest.php b/lib/silex/vendor/twig/twig/test/Twig/Tests/Node/Expression/CallTest.php index 43afcd292..883bb6127 100644 --- a/lib/silex/vendor/twig/twig/test/Twig/Tests/Node/Expression/CallTest.php +++ b/lib/silex/vendor/twig/twig/test/Twig/Tests/Node/Expression/CallTest.php @@ -9,7 +9,7 @@ * file that was distributed with this source code. */ -class Twig_Tests_Node_Expression_CallTest extends PHPUnit_Framework_TestCase +class Twig_Tests_Node_Expression_CallTest extends \PHPUnit\Framework\TestCase { public function testGetArguments() { @@ -63,10 +63,6 @@ class Twig_Tests_Node_Expression_CallTest extends PHPUnit_Framework_TestCase */ public function testResolveArgumentsWithMissingValueForOptionalArgument() { - if (defined('HHVM_VERSION')) { - $this->markTestSkipped('Skip under HHVM as the behavior is not the same as plain PHP (which is an edge case anyway)'); - } - $node = new Twig_Tests_Node_Expression_Call(array(), array('type' => 'function', 'name' => 'substr_compare')); $node->getArguments('substr_compare', array('abcd', 'bc', 'offset' => 1, 'case_sensitivity' => true)); } @@ -105,6 +101,26 @@ class Twig_Tests_Node_Expression_CallTest extends PHPUnit_Framework_TestCase public function customFunctionWithArbitraryArguments() { } + + /** + * @expectedException LogicException + * @expectedExceptionMessageRegExp #^The last parameter of "custom_Twig_Tests_Node_Expression_CallTest_function" for function "foo" must be an array with default value, eg\. "array \$arg \= array\(\)"\.$# + */ + public function testResolveArgumentsWithMissingParameterForArbitraryArgumentsOnFunction() + { + $node = new Twig_Tests_Node_Expression_Call(array(), array('type' => 'function', 'name' => 'foo', 'is_variadic' => true)); + $node->getArguments('custom_Twig_Tests_Node_Expression_CallTest_function', array()); + } + + /** + * @expectedException LogicException + * @expectedExceptionMessageRegExp #^The last parameter of "CallableTestClass\:\:__invoke" for function "foo" must be an array with default value, eg\. "array \$arg \= array\(\)"\.$# + */ + public function testResolveArgumentsWithMissingParameterForArbitraryArgumentsOnObject() + { + $node = new Twig_Tests_Node_Expression_Call(array(), array('type' => 'function', 'name' => 'foo', 'is_variadic' => true)); + $node->getArguments(new CallableTestClass(), array()); + } } class Twig_Tests_Node_Expression_Call extends Twig_Node_Expression_Call @@ -114,3 +130,14 @@ class Twig_Tests_Node_Expression_Call extends Twig_Node_Expression_Call return parent::getArguments($callable, $arguments); } } + +class CallableTestClass +{ + public function __invoke($required) + { + } +} + +function custom_Twig_Tests_Node_Expression_CallTest_function($required) +{ +} diff --git a/lib/silex/vendor/twig/twig/test/Twig/Tests/Node/Expression/FilterTest.php b/lib/silex/vendor/twig/twig/test/Twig/Tests/Node/Expression/FilterTest.php index d5ffb244d..773375c94 100644 --- a/lib/silex/vendor/twig/twig/test/Twig/Tests/Node/Expression/FilterTest.php +++ b/lib/silex/vendor/twig/twig/test/Twig/Tests/Node/Expression/FilterTest.php @@ -25,7 +25,7 @@ class Twig_Tests_Node_Expression_FilterTest extends Twig_Test_NodeTestCase public function getTests() { - $environment = new Twig_Environment($this->getMock('Twig_LoaderInterface')); + $environment = new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock()); $environment->addFilter(new Twig_SimpleFilter('bar', 'bar', array('needs_environment' => true))); $environment->addFilter(new Twig_SimpleFilter('barbar', 'twig_tests_filter_barbar', array('needs_context' => true, 'is_variadic' => true))); diff --git a/lib/silex/vendor/twig/twig/test/Twig/Tests/Node/Expression/FunctionTest.php b/lib/silex/vendor/twig/twig/test/Twig/Tests/Node/Expression/FunctionTest.php index de2e0f8ee..2e82e2f11 100644 --- a/lib/silex/vendor/twig/twig/test/Twig/Tests/Node/Expression/FunctionTest.php +++ b/lib/silex/vendor/twig/twig/test/Twig/Tests/Node/Expression/FunctionTest.php @@ -23,7 +23,7 @@ class Twig_Tests_Node_Expression_FunctionTest extends Twig_Test_NodeTestCase public function getTests() { - $environment = new Twig_Environment($this->getMock('Twig_LoaderInterface')); + $environment = new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock()); $environment->addFunction(new Twig_SimpleFunction('foo', 'foo', array())); $environment->addFunction(new Twig_SimpleFunction('bar', 'bar', array('needs_environment' => true))); $environment->addFunction(new Twig_SimpleFunction('foofoo', 'foofoo', array('needs_context' => true))); diff --git a/lib/silex/vendor/twig/twig/test/Twig/Tests/Node/Expression/NameTest.php b/lib/silex/vendor/twig/twig/test/Twig/Tests/Node/Expression/NameTest.php index 8cbb2f764..70721a8bf 100644 --- a/lib/silex/vendor/twig/twig/test/Twig/Tests/Node/Expression/NameTest.php +++ b/lib/silex/vendor/twig/twig/test/Twig/Tests/Node/Expression/NameTest.php @@ -23,11 +23,19 @@ class Twig_Tests_Node_Expression_NameTest extends Twig_Test_NodeTestCase $node = new Twig_Node_Expression_Name('foo', 1); $context = new Twig_Node_Expression_Name('_context', 1); - $env = new Twig_Environment($this->getMock('Twig_LoaderInterface'), array('strict_variables' => true)); - $env1 = new Twig_Environment($this->getMock('Twig_LoaderInterface'), array('strict_variables' => false)); + $env = new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock(), array('strict_variables' => true)); + $env1 = new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock(), array('strict_variables' => false)); + + if (PHP_VERSION_ID >= 70000) { + $output = '($context["foo"] ?? $this->getContext($context, "foo"))'; + } elseif (PHP_VERSION_ID >= 50400) { + $output = '(isset($context["foo"]) ? $context["foo"] : $this->getContext($context, "foo"))'; + } else { + $output = '$this->getContext($context, "foo")'; + } return array( - array($node, "// line 1\n".(PHP_VERSION_ID >= 50400 ? '(isset($context["foo"]) ? $context["foo"] : $this->getContext($context, "foo"))' : '$this->getContext($context, "foo")'), $env), + array($node, "// line 1\n".$output, $env), array($node, $this->getVariableGetter('foo', 1), $env1), array($context, "// line 1\n\$context"), ); diff --git a/lib/silex/vendor/twig/twig/test/Twig/Tests/Node/Expression/NullCoalesceTest.php b/lib/silex/vendor/twig/twig/test/Twig/Tests/Node/Expression/NullCoalesceTest.php new file mode 100644 index 000000000..a37490baf --- /dev/null +++ b/lib/silex/vendor/twig/twig/test/Twig/Tests/Node/Expression/NullCoalesceTest.php @@ -0,0 +1,31 @@ += 70000) { + $tests[] = array($node, "((// line 1\n\$context[\"foo\"]) ?? (2))"); + } elseif (PHP_VERSION_ID >= 50400) { + $tests[] = array($node, "(((// line 1\narray_key_exists(\"foo\", \$context) && !(null === (isset(\$context[\"foo\"]) ? \$context[\"foo\"] : null)))) ? ((isset(\$context[\"foo\"]) ? \$context[\"foo\"] : null)) : (2))"); + } else { + $tests[] = array($node, "(((// line 1\narray_key_exists(\"foo\", \$context) && !(null === \$this->getContext(\$context, \"foo\")))) ? (\$this->getContext(\$context, \"foo\")) : (2))"); + } + + return $tests; + } +} diff --git a/lib/silex/vendor/twig/twig/test/Twig/Tests/Node/Expression/TestTest.php b/lib/silex/vendor/twig/twig/test/Twig/Tests/Node/Expression/TestTest.php index 55d3fcbf6..a5f96d245 100644 --- a/lib/silex/vendor/twig/twig/test/Twig/Tests/Node/Expression/TestTest.php +++ b/lib/silex/vendor/twig/twig/test/Twig/Tests/Node/Expression/TestTest.php @@ -25,7 +25,7 @@ class Twig_Tests_Node_Expression_TestTest extends Twig_Test_NodeTestCase public function getTests() { - $environment = new Twig_Environment($this->getMock('Twig_LoaderInterface')); + $environment = new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock()); $environment->addTest(new Twig_SimpleTest('barbar', 'twig_tests_test_barbar', array('is_variadic' => true, 'need_context' => true))); $tests = array(); diff --git a/lib/silex/vendor/twig/twig/test/Twig/Tests/Node/ForTest.php b/lib/silex/vendor/twig/twig/test/Twig/Tests/Node/ForTest.php index b289592fb..2bf4c7b43 100644 --- a/lib/silex/vendor/twig/twig/test/Twig/Tests/Node/ForTest.php +++ b/lib/silex/vendor/twig/twig/test/Twig/Tests/Node/ForTest.php @@ -28,7 +28,7 @@ class Twig_Tests_Node_ForTest extends Twig_Test_NodeTestCase $this->assertTrue($node->getAttribute('ifexpr')); $this->assertEquals('Twig_Node_If', get_class($node->getNode('body'))); $this->assertEquals($body, $node->getNode('body')->getNode('tests')->getNode(1)->getNode(0)); - $this->assertNull($node->getNode('else')); + $this->assertFalse($node->hasNode('else')); $else = new Twig_Node_Print(new Twig_Node_Expression_Name('foo', 1), 1); $node = new Twig_Node_For($keyTarget, $valueTarget, $seq, $ifexpr, $body, $else, 1); @@ -51,7 +51,7 @@ class Twig_Tests_Node_ForTest extends Twig_Test_NodeTestCase $tests[] = array($node, <<getVariableGetter('items')}); foreach (\$context['_seq'] as \$context["key"] => \$context["item"]) { echo {$this->getVariableGetter('foo')}; @@ -73,7 +73,7 @@ EOF $tests[] = array($node, <<getVariableGetter('values')}); \$context['loop'] = array( 'parent' => \$context['_parent'], @@ -116,7 +116,7 @@ EOF $tests[] = array($node, <<getVariableGetter('values')}); \$context['loop'] = array( 'parent' => \$context['_parent'], @@ -149,7 +149,7 @@ EOF $tests[] = array($node, <<getVariableGetter('values')}); \$context['_iterated'] = false; \$context['loop'] = array( diff --git a/lib/silex/vendor/twig/twig/test/Twig/Tests/Node/IfTest.php b/lib/silex/vendor/twig/twig/test/Twig/Tests/Node/IfTest.php index e47dd6540..4ab0e4cc7 100644 --- a/lib/silex/vendor/twig/twig/test/Twig/Tests/Node/IfTest.php +++ b/lib/silex/vendor/twig/twig/test/Twig/Tests/Node/IfTest.php @@ -21,7 +21,7 @@ class Twig_Tests_Node_IfTest extends Twig_Test_NodeTestCase $node = new Twig_Node_If($t, $else, 1); $this->assertEquals($t, $node->getNode('tests')); - $this->assertNull($node->getNode('else')); + $this->assertFalse($node->hasNode('else')); $else = new Twig_Node_Print(new Twig_Node_Expression_Name('bar', 1), 1); $node = new Twig_Node_If($t, $else, 1); diff --git a/lib/silex/vendor/twig/twig/test/Twig/Tests/Node/IncludeTest.php b/lib/silex/vendor/twig/twig/test/Twig/Tests/Node/IncludeTest.php index 6fe5c17bf..d801f3387 100644 --- a/lib/silex/vendor/twig/twig/test/Twig/Tests/Node/IncludeTest.php +++ b/lib/silex/vendor/twig/twig/test/Twig/Tests/Node/IncludeTest.php @@ -16,7 +16,7 @@ class Twig_Tests_Node_IncludeTest extends Twig_Test_NodeTestCase $expr = new Twig_Node_Expression_Constant('foo.twig', 1); $node = new Twig_Node_Include($expr, null, false, false, 1); - $this->assertNull($node->getNode('variables')); + $this->assertFalse($node->hasNode('variables')); $this->assertEquals($expr, $node->getNode('expr')); $this->assertFalse($node->getAttribute('only')); diff --git a/lib/silex/vendor/twig/twig/test/Twig/Tests/Node/MacroTest.php b/lib/silex/vendor/twig/twig/test/Twig/Tests/Node/MacroTest.php index 901e57b91..c7edfa251 100644 --- a/lib/silex/vendor/twig/twig/test/Twig/Tests/Node/MacroTest.php +++ b/lib/silex/vendor/twig/twig/test/Twig/Tests/Node/MacroTest.php @@ -58,6 +58,10 @@ public function getfoo(\$__foo__ = null, \$__bar__ = "Foo"$declaration) } catch (Exception \$e) { ob_end_clean(); + throw \$e; + } catch (Throwable \$e) { + ob_end_clean(); + throw \$e; } diff --git a/lib/silex/vendor/twig/twig/test/Twig/Tests/Node/ModuleTest.php b/lib/silex/vendor/twig/twig/test/Twig/Tests/Node/ModuleTest.php index 5688af8cc..54a8989c7 100644 --- a/lib/silex/vendor/twig/twig/test/Twig/Tests/Node/ModuleTest.php +++ b/lib/silex/vendor/twig/twig/test/Twig/Tests/Node/ModuleTest.php @@ -18,19 +18,19 @@ class Twig_Tests_Node_ModuleTest extends Twig_Test_NodeTestCase $blocks = new Twig_Node(); $macros = new Twig_Node(); $traits = new Twig_Node(); - $filename = 'foo.twig'; - $node = new Twig_Node_Module($body, $parent, $blocks, $macros, $traits, new Twig_Node(array()), $filename); + $source = new Twig_Source('{{ foo }}', 'foo.twig'); + $node = new Twig_Node_Module($body, $parent, $blocks, $macros, $traits, new Twig_Node(array()), $source); $this->assertEquals($body, $node->getNode('body')); $this->assertEquals($blocks, $node->getNode('blocks')); $this->assertEquals($macros, $node->getNode('macros')); $this->assertEquals($parent, $node->getNode('parent')); - $this->assertEquals($filename, $node->getAttribute('filename')); + $this->assertEquals($source->getName(), $node->getTemplateName()); } public function getTests() { - $twig = new Twig_Environment($this->getMock('Twig_LoaderInterface')); + $twig = new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock()); $tests = array(); @@ -39,14 +39,14 @@ class Twig_Tests_Node_ModuleTest extends Twig_Test_NodeTestCase $blocks = new Twig_Node(); $macros = new Twig_Node(); $traits = new Twig_Node(); - $filename = 'foo.twig'; + $source = new Twig_Source('{{ foo }}', 'foo.twig'); - $node = new Twig_Node_Module($body, $extends, $blocks, $macros, $traits, new Twig_Node(array()), $filename); + $node = new Twig_Node_Module($body, $extends, $blocks, $macros, $traits, new Twig_Node(array()), $source); $tests[] = array($node, << 1,); } + + /** @deprecated since 1.27 (to be removed in 2.0). Use getSourceContext() instead */ + public function getSource() + { + @trigger_error('The '.__METHOD__.' method is deprecated since version 1.27 and will be removed in 2.0. Use getSourceContext() instead.', E_USER_DEPRECATED); + + return \$this->getSourceContext()->getCode(); + } + + public function getSourceContext() + { + return new Twig_Source("", "foo.twig", ""); + } } EOF - , $twig); + , $twig, true); $import = new Twig_Node_Import(new Twig_Node_Expression_Constant('foo.twig', 1), new Twig_Node_Expression_AssignName('macro', 1), 2); $body = new Twig_Node(array($import)); $extends = new Twig_Node_Expression_Constant('layout.twig', 1); - $node = new Twig_Node_Module($body, $extends, $blocks, $macros, $traits, new Twig_Node(array()), $filename); + $node = new Twig_Node_Module($body, $extends, $blocks, $macros, $traits, new Twig_Node(array()), $source); $tests[] = array($node, << 1, 24 => 2, 11 => 1,); } + + /** @deprecated since 1.27 (to be removed in 2.0). Use getSourceContext() instead */ + public function getSource() + { + @trigger_error('The '.__METHOD__.' method is deprecated since version 1.27 and will be removed in 2.0. Use getSourceContext() instead.', E_USER_DEPRECATED); + + return \$this->getSourceContext()->getCode(); + } + + public function getSourceContext() + { + return new Twig_Source("", "foo.twig", ""); + } } EOF - , $twig); + , $twig, true); $set = new Twig_Node_Set(false, new Twig_Node(array(new Twig_Node_Expression_AssignName('foo', 4))), new Twig_Node(array(new Twig_Node_Expression_Constant('foo', 4))), 4); $body = new Twig_Node(array($set)); @@ -139,12 +165,13 @@ EOF 2 ); - $node = new Twig_Node_Module($body, $extends, $blocks, $macros, $traits, new Twig_Node(array()), $filename); + $twig = new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock(), array('debug' => true)); + $node = new Twig_Node_Module($body, $extends, $blocks, $macros, $traits, new Twig_Node(array()), $source); $tests[] = array($node, << 2, 15 => 4, 9 => 2,); } + + /** @deprecated since 1.27 (to be removed in 2.0). Use getSourceContext() instead */ + public function getSource() + { + @trigger_error('The '.__METHOD__.' method is deprecated since version 1.27 and will be removed in 2.0. Use getSourceContext() instead.', E_USER_DEPRECATED); + + return \$this->getSourceContext()->getCode(); + } + + public function getSourceContext() + { + return new Twig_Source("{{ foo }}", "foo.twig", ""); + } } EOF - , $twig); + , $twig, true); return $tests; } diff --git a/lib/silex/vendor/twig/twig/test/Twig/Tests/Node/SandboxTest.php b/lib/silex/vendor/twig/twig/test/Twig/Tests/Node/SandboxTest.php index 46ecf9731..56f487730 100644 --- a/lib/silex/vendor/twig/twig/test/Twig/Tests/Node/SandboxTest.php +++ b/lib/silex/vendor/twig/twig/test/Twig/Tests/Node/SandboxTest.php @@ -28,7 +28,7 @@ class Twig_Tests_Node_SandboxTest extends Twig_Test_NodeTestCase $tests[] = array($node, <<env->getExtension('sandbox'); +\$sandbox = \$this->env->getExtension('Twig_Extension_Sandbox'); if (!\$alreadySandboxed = \$sandbox->isSandboxed()) { \$sandbox->enableSandbox(); } diff --git a/lib/silex/vendor/twig/twig/test/Twig/Tests/Node/SandboxedPrintTest.php b/lib/silex/vendor/twig/twig/test/Twig/Tests/Node/SandboxedPrintTest.php index 05e1854cb..8bc8a755c 100644 --- a/lib/silex/vendor/twig/twig/test/Twig/Tests/Node/SandboxedPrintTest.php +++ b/lib/silex/vendor/twig/twig/test/Twig/Tests/Node/SandboxedPrintTest.php @@ -24,7 +24,7 @@ class Twig_Tests_Node_SandboxedPrintTest extends Twig_Test_NodeTestCase $tests[] = array(new Twig_Node_SandboxedPrint(new Twig_Node_Expression_Constant('foo', 1), 1), <<env->getExtension('sandbox')->ensureToStringAllowed("foo"); +echo \$this->env->getExtension('Twig_Extension_Sandbox')->ensureToStringAllowed("foo"); EOF ); diff --git a/lib/silex/vendor/twig/twig/test/Twig/Tests/NodeVisitor/OptimizerTest.php b/lib/silex/vendor/twig/twig/test/Twig/Tests/NodeVisitor/OptimizerTest.php index b5ea7aac2..92c0ecac7 100644 --- a/lib/silex/vendor/twig/twig/test/Twig/Tests/NodeVisitor/OptimizerTest.php +++ b/lib/silex/vendor/twig/twig/test/Twig/Tests/NodeVisitor/OptimizerTest.php @@ -8,13 +8,13 @@ * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -class Twig_Tests_NodeVisitor_OptimizerTest extends PHPUnit_Framework_TestCase +class Twig_Tests_NodeVisitor_OptimizerTest extends \PHPUnit\Framework\TestCase { public function testRenderBlockOptimizer() { - $env = new Twig_Environment($this->getMock('Twig_LoaderInterface'), array('cache' => false, 'autoescape' => false)); + $env = new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock(), array('cache' => false, 'autoescape' => false)); - $stream = $env->parse($env->tokenize('{{ block("foo") }}', 'index')); + $stream = $env->parse($env->tokenize(new Twig_Source('{{ block("foo") }}', 'index'))); $node = $stream->getNode('body')->getNode(0); @@ -24,9 +24,9 @@ class Twig_Tests_NodeVisitor_OptimizerTest extends PHPUnit_Framework_TestCase public function testRenderParentBlockOptimizer() { - $env = new Twig_Environment($this->getMock('Twig_LoaderInterface'), array('cache' => false, 'autoescape' => false)); + $env = new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock(), array('cache' => false, 'autoescape' => false)); - $stream = $env->parse($env->tokenize('{% extends "foo" %}{% block content %}{{ parent() }}{% endblock %}', 'index')); + $stream = $env->parse($env->tokenize(new Twig_Source('{% extends "foo" %}{% block content %}{{ parent() }}{% endblock %}', 'index'))); $node = $stream->getNode('blocks')->getNode('content')->getNode(0)->getNode('body'); @@ -37,11 +37,11 @@ class Twig_Tests_NodeVisitor_OptimizerTest extends PHPUnit_Framework_TestCase public function testRenderVariableBlockOptimizer() { if (PHP_VERSION_ID >= 50400) { - return; + $this->markTestSkipped('not needed on PHP >= 5.4'); } - $env = new Twig_Environment($this->getMock('Twig_LoaderInterface'), array('cache' => false, 'autoescape' => false)); - $stream = $env->parse($env->tokenize('{{ block(name|lower) }}', 'index')); + $env = new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock(), array('cache' => false, 'autoescape' => false)); + $stream = $env->parse($env->tokenize(new Twig_Source('{{ block(name|lower) }}', 'index'))); $node = $stream->getNode('body')->getNode(0)->getNode(1); @@ -54,9 +54,9 @@ class Twig_Tests_NodeVisitor_OptimizerTest extends PHPUnit_Framework_TestCase */ public function testForOptimizer($template, $expected) { - $env = new Twig_Environment($this->getMock('Twig_LoaderInterface'), array('cache' => false)); + $env = new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock(), array('cache' => false)); - $stream = $env->parse($env->tokenize($template, 'index')); + $stream = $env->parse($env->tokenize(new Twig_Source($template, 'index'))); foreach ($expected as $target => $withLoop) { $this->assertTrue($this->checkForConfiguration($stream, $target, $withLoop), sprintf('variable %s is %soptimized', $target, $withLoop ? 'not ' : '')); diff --git a/lib/silex/vendor/twig/twig/test/Twig/Tests/ParserTest.php b/lib/silex/vendor/twig/twig/test/Twig/Tests/ParserTest.php index b29dac311..342fb7bab 100644 --- a/lib/silex/vendor/twig/twig/test/Twig/Tests/ParserTest.php +++ b/lib/silex/vendor/twig/twig/test/Twig/Tests/ParserTest.php @@ -8,7 +8,7 @@ * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -class Twig_Tests_ParserTest extends PHPUnit_Framework_TestCase +class Twig_Tests_ParserTest extends \PHPUnit\Framework\TestCase { /** * @expectedException Twig_Error_Syntax @@ -16,12 +16,12 @@ class Twig_Tests_ParserTest extends PHPUnit_Framework_TestCase public function testSetMacroThrowsExceptionOnReservedMethods() { $parser = $this->getParser(); - $parser->setMacro('parent', $this->getMock('Twig_Node_Macro', array(), array(), '', null)); + $parser->setMacro('parent', $this->getMockBuilder('Twig_Node_Macro')->disableOriginalConstructor()->getMock()); } /** * @expectedException Twig_Error_Syntax - * @expectedExceptionMessage Unknown tag name "foo". Did you mean "for" at line 1 + * @expectedExceptionMessage Unknown "foo" tag. Did you mean "for" at line 1? */ public function testUnknownTag() { @@ -31,7 +31,23 @@ class Twig_Tests_ParserTest extends PHPUnit_Framework_TestCase new Twig_Token(Twig_Token::BLOCK_END_TYPE, '', 1), new Twig_Token(Twig_Token::EOF_TYPE, '', 1), )); - $parser = new Twig_Parser(new Twig_Environment($this->getMock('Twig_LoaderInterface'))); + $parser = new Twig_Parser(new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock())); + $parser->parse($stream); + } + + /** + * @expectedException Twig_Error_Syntax + * @expectedExceptionMessage Unknown "foobar" tag at line 1. + */ + public function testUnknownTagWithoutSuggestions() + { + $stream = new Twig_TokenStream(array( + new Twig_Token(Twig_Token::BLOCK_START_TYPE, '', 1), + new Twig_Token(Twig_Token::NAME_TYPE, 'foobar', 1), + new Twig_Token(Twig_Token::BLOCK_END_TYPE, '', 1), + new Twig_Token(Twig_Token::EOF_TYPE, '', 1), + )); + $parser = new Twig_Parser(new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock())); $parser->parse($stream); } @@ -84,7 +100,7 @@ class Twig_Tests_ParserTest extends PHPUnit_Framework_TestCase /** * @expectedException Twig_Error_Syntax - * @expectedExceptionMessage A template that extends another one cannot have a body but a byte order mark (BOM) has been detected; it must be removed at line 1. + * @expectedExceptionMessage A template that extends another one cannot start with a byte order mark (BOM); it must be removed at line 1 */ public function testFilterBodyNodesWithBOM() { @@ -94,7 +110,7 @@ class Twig_Tests_ParserTest extends PHPUnit_Framework_TestCase public function testParseIsReentrant() { - $twig = new Twig_Environment($this->getMock('Twig_LoaderInterface'), array( + $twig = new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock(), array( 'autoescape' => false, 'optimizations' => 0, )); @@ -115,31 +131,33 @@ class Twig_Tests_ParserTest extends PHPUnit_Framework_TestCase $this->assertNull($parser->getParent()); } - // The getVarName() must not depend on the template loaders, - // If this test does not throw any exception, that's good. - // see https://github.com/symfony/symfony/issues/4218 public function testGetVarName() { - $twig = new Twig_Environment($this->getMock('Twig_LoaderInterface'), array( + $twig = new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock(), array( 'autoescape' => false, 'optimizations' => 0, )); - $twig->parse($twig->tokenize(<<parse($twig->tokenize(new Twig_Source(<<addToAssertionCount(1); } protected function getParser() { - $parser = new TestParser(new Twig_Environment($this->getMock('Twig_LoaderInterface'))); + $parser = new TestParser(new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock())); $parser->setParent(new Twig_Node()); - $parser->stream = $this->getMockBuilder('Twig_TokenStream')->disableOriginalConstructor()->getMock(); + $parser->stream = new Twig_TokenStream(array()); return $parser; } diff --git a/lib/silex/vendor/twig/twig/test/Twig/Tests/Profiler/Dumper/AbstractTest.php b/lib/silex/vendor/twig/twig/test/Twig/Tests/Profiler/Dumper/AbstractTest.php index da97f478a..a71b97b97 100644 --- a/lib/silex/vendor/twig/twig/test/Twig/Tests/Profiler/Dumper/AbstractTest.php +++ b/lib/silex/vendor/twig/twig/test/Twig/Tests/Profiler/Dumper/AbstractTest.php @@ -9,7 +9,7 @@ * file that was distributed with this source code. */ -abstract class Twig_Tests_Profiler_Dumper_AbstractTest extends PHPUnit_Framework_TestCase +abstract class Twig_Tests_Profiler_Dumper_AbstractTest extends \PHPUnit\Framework\TestCase { protected function getProfile() { diff --git a/lib/silex/vendor/twig/twig/test/Twig/Tests/Profiler/ProfileTest.php b/lib/silex/vendor/twig/twig/test/Twig/Tests/Profiler/ProfileTest.php index f786f06ca..08db96a90 100644 --- a/lib/silex/vendor/twig/twig/test/Twig/Tests/Profiler/ProfileTest.php +++ b/lib/silex/vendor/twig/twig/test/Twig/Tests/Profiler/ProfileTest.php @@ -9,7 +9,7 @@ * file that was distributed with this source code. */ -class Twig_Tests_Profiler_ProfileTest extends PHPUnit_Framework_TestCase +class Twig_Tests_Profiler_ProfileTest extends \PHPUnit\Framework\TestCase { public function testConstructor() { @@ -97,4 +97,14 @@ class Twig_Tests_Profiler_ProfileTest extends PHPUnit_Framework_TestCase $this->assertEquals($profile1->getType(), $profile3->getType()); $this->assertEquals($profile1->getName(), $profile3->getName()); } + + public function testReset() + { + $profile = new Twig_Profiler_Profile(); + usleep(1); + $profile->leave(); + $profile->reset(); + + $this->assertEquals(0, $profile->getDuration()); + } } diff --git a/lib/silex/vendor/twig/twig/test/Twig/Tests/TemplateTest.php b/lib/silex/vendor/twig/twig/test/Twig/Tests/TemplateTest.php index 31a844bd7..c22a43336 100644 --- a/lib/silex/vendor/twig/twig/test/Twig/Tests/TemplateTest.php +++ b/lib/silex/vendor/twig/twig/test/Twig/Tests/TemplateTest.php @@ -8,7 +8,7 @@ * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -class Twig_Tests_TemplateTest extends PHPUnit_Framework_TestCase +class Twig_Tests_TemplateTest extends \PHPUnit\Framework\TestCase { /** * @expectedException LogicException @@ -22,18 +22,11 @@ class Twig_Tests_TemplateTest extends PHPUnit_Framework_TestCase /** * @dataProvider getAttributeExceptions */ - public function testGetAttributeExceptions($template, $message, $useExt) + public function testGetAttributeExceptions($template, $message) { - $name = 'index_'.($useExt ? 1 : 0); - $templates = array( - $name => $template.$useExt, // appending $useExt makes the template content unique - ); - + $templates = array('index' => $template); $env = new Twig_Environment(new Twig_Loader_Array($templates), array('strict_variables' => true)); - if (!$useExt) { - $env->addNodeVisitor(new CExtDisablingNodeVisitor()); - } - $template = $env->loadTemplate($name); + $template = $env->loadTemplate('index'); $context = array( 'string' => 'foo', @@ -49,60 +42,55 @@ class Twig_Tests_TemplateTest extends PHPUnit_Framework_TestCase $template->render($context); $this->fail('Accessing an invalid attribute should throw an exception.'); } catch (Twig_Error_Runtime $e) { - $this->assertSame(sprintf($message, $name), $e->getMessage()); + $this->assertSame(sprintf($message, 'index'), $e->getMessage()); } } public function getAttributeExceptions() { - $tests = array( - array('{{ string["a"] }}', 'Impossible to access a key ("a") on a string variable ("foo") in "%s" at line 1', false), - array('{{ null["a"] }}', 'Impossible to access a key ("a") on a null variable in "%s" at line 1', false), - array('{{ empty_array["a"] }}', 'Key "a" does not exist as the array is empty in "%s" at line 1', false), - array('{{ array["a"] }}', 'Key "a" for array with keys "foo" does not exist in "%s" at line 1', false), - array('{{ array_access["a"] }}', 'Key "a" in object with ArrayAccess of class "Twig_TemplateArrayAccessObject" does not exist in "%s" at line 1', false), - array('{{ string.a }}', 'Impossible to access an attribute ("a") on a string variable ("foo") in "%s" at line 1', false), - array('{{ string.a() }}', 'Impossible to invoke a method ("a") on a string variable ("foo") in "%s" at line 1', false), - array('{{ null.a }}', 'Impossible to access an attribute ("a") on a null variable in "%s" at line 1', false), - array('{{ null.a() }}', 'Impossible to invoke a method ("a") on a null variable in "%s" at line 1', false), - array('{{ empty_array.a }}', 'Key "a" does not exist as the array is empty in "%s" at line 1', false), - array('{{ array.a }}', 'Key "a" for array with keys "foo" does not exist in "%s" at line 1', false), - array('{{ attribute(array, -10) }}', 'Key "-10" for array with keys "foo" does not exist in "%s" at line 1', false), - array('{{ array_access.a }}', 'Method "a" for object "Twig_TemplateArrayAccessObject" does not exist in "%s" at line 1', false), - array('{% from _self import foo %}{% macro foo(obj) %}{{ obj.missing_method() }}{% endmacro %}{{ foo(array_access) }}', 'Method "missing_method" for object "Twig_TemplateArrayAccessObject" does not exist in "%s" at line 1', false), - array('{{ magic_exception.test }}', 'An exception has been thrown during the rendering of a template ("Hey! Don\'t try to isset me!") in "%s" at line 1.', false), - array('{{ object["a"] }}', 'Impossible to access a key "a" on an object of class "stdClass" that does not implement ArrayAccess interface in "%s" at line 1', false), + return array( + array('{{ string["a"] }}', 'Impossible to access a key ("a") on a string variable ("foo") in "%s" at line 1.'), + array('{{ null["a"] }}', 'Impossible to access a key ("a") on a null variable in "%s" at line 1.'), + array('{{ empty_array["a"] }}', 'Key "a" does not exist as the array is empty in "%s" at line 1.'), + array('{{ array["a"] }}', 'Key "a" for array with keys "foo" does not exist in "%s" at line 1.'), + array('{{ array_access["a"] }}', 'Key "a" in object with ArrayAccess of class "Twig_TemplateArrayAccessObject" does not exist in "%s" at line 1.'), + array('{{ string.a }}', 'Impossible to access an attribute ("a") on a string variable ("foo") in "%s" at line 1.'), + array('{{ string.a() }}', 'Impossible to invoke a method ("a") on a string variable ("foo") in "%s" at line 1.'), + array('{{ null.a }}', 'Impossible to access an attribute ("a") on a null variable in "%s" at line 1.'), + array('{{ null.a() }}', 'Impossible to invoke a method ("a") on a null variable in "%s" at line 1.'), + array('{{ empty_array.a }}', 'Key "a" does not exist as the array is empty in "%s" at line 1.'), + array('{{ array.a }}', 'Key "a" for array with keys "foo" does not exist in "%s" at line 1.'), + array('{{ attribute(array, -10) }}', 'Key "-10" for array with keys "foo" does not exist in "%s" at line 1.'), + array('{{ array_access.a }}', 'Neither the property "a" nor one of the methods "a()", "geta()"/"isa()" or "__call()" exist and have public access in class "Twig_TemplateArrayAccessObject" in "%s" at line 1.'), + array('{% from _self import foo %}{% macro foo(obj) %}{{ obj.missing_method() }}{% endmacro %}{{ foo(array_access) }}', 'Neither the property "missing_method" nor one of the methods "missing_method()", "getmissing_method()"/"ismissing_method()" or "__call()" exist and have public access in class "Twig_TemplateArrayAccessObject" in "%s" at line 1.'), + array('{{ magic_exception.test }}', 'An exception has been thrown during the rendering of a template ("Hey! Don\'t try to isset me!") in "%s" at line 1.'), + array('{{ object["a"] }}', 'Impossible to access a key "a" on an object of class "stdClass" that does not implement ArrayAccess interface in "%s" at line 1.'), ); - - if (function_exists('twig_template_get_attributes')) { - foreach (array_slice($tests, 0) as $test) { - $test[2] = true; - $tests[] = $test; - } - } - - return $tests; } /** * @dataProvider getGetAttributeWithSandbox */ - public function testGetAttributeWithSandbox($object, $item, $allowed, $useExt) + public function testGetAttributeWithSandbox($object, $item, $allowed) { - $twig = new Twig_Environment($this->getMock('Twig_LoaderInterface')); + $twig = new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock()); $policy = new Twig_Sandbox_SecurityPolicy(array(), array(), array(/*method*/), array(/*prop*/), array()); $twig->addExtension(new Twig_Extension_Sandbox($policy, !$allowed)); - $template = new Twig_TemplateTest($twig, $useExt); + $template = new Twig_TemplateTest($twig); try { $template->getAttribute($object, $item, array(), 'any'); if (!$allowed) { $this->fail(); + } else { + $this->addToAssertionCount(1); } } catch (Twig_Sandbox_SecurityError $e) { if ($allowed) { $this->fail(); + } else { + $this->addToAssertionCount(1); } $this->assertContains('is not allowed', $e->getMessage()); @@ -111,30 +99,25 @@ class Twig_Tests_TemplateTest extends PHPUnit_Framework_TestCase public function getGetAttributeWithSandbox() { - $tests = array( - array(new Twig_TemplatePropertyObject(), 'defined', false, false), - array(new Twig_TemplatePropertyObject(), 'defined', true, false), - array(new Twig_TemplateMethodObject(), 'defined', false, false), - array(new Twig_TemplateMethodObject(), 'defined', true, false), + return array( + array(new Twig_TemplatePropertyObject(), 'defined', false), + array(new Twig_TemplatePropertyObject(), 'defined', true), + array(new Twig_TemplateMethodObject(), 'defined', false), + array(new Twig_TemplateMethodObject(), 'defined', true), ); - - if (function_exists('twig_template_get_attributes')) { - foreach (array_slice($tests, 0) as $test) { - $test[3] = true; - $tests[] = $test; - } - } - - return $tests; } /** - * @dataProvider getGetAttributeWithTemplateAsObject + * @group legacy */ - public function testGetAttributeWithTemplateAsObject($useExt) + public function testGetAttributeWithTemplateAsObject() { - $template = new Twig_TemplateTest(new Twig_Environment($this->getMock('Twig_LoaderInterface')), $useExt); - $template1 = new Twig_TemplateTest(new Twig_Environment($this->getMock('Twig_LoaderInterface')), false); + // to be removed in 2.0 + $twig = new Twig_Environment($this->getMockBuilder('Twig_TemplateTestLoaderInterface')->getMock()); + //$twig = new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface', 'Twig_SourceContextLoaderInterface')->getMock()); + + $template = new Twig_TemplateTest($twig, 'index.twig'); + $template1 = new Twig_TemplateTest($twig, 'index1.twig'); $this->assertInstanceof('Twig_Markup', $template->getAttribute($template1, 'string')); $this->assertEquals('some_string', $template->getAttribute($template1, 'string')); @@ -154,28 +137,86 @@ class Twig_Tests_TemplateTest extends PHPUnit_Framework_TestCase $this->assertFalse($template->getAttribute($template1, 'displayWithErrorHandling', array(), Twig_Template::METHOD_CALL, true)); } - public function getGetAttributeWithTemplateAsObject() + /** + * @group legacy + * @expectedDeprecation Calling "string" on template "index1.twig" from template "index.twig" is deprecated since version 1.28 and won't be supported anymore in 2.0. + * @expectedDeprecation Calling "string" on template "index1.twig" from template "index.twig" is deprecated since version 1.28 and won't be supported anymore in 2.0. + * @expectedDeprecation Calling "true" on template "index1.twig" from template "index.twig" is deprecated since version 1.28 and won't be supported anymore in 2.0. + * @expectedDeprecation Calling "true" on template "index1.twig" from template "index.twig" is deprecated since version 1.28 and won't be supported anymore in 2.0. + * @expectedDeprecation Calling "zero" on template "index1.twig" from template "index.twig" is deprecated since version 1.28 and won't be supported anymore in 2.0. + * @expectedDeprecation Calling "zero" on template "index1.twig" from template "index.twig" is deprecated since version 1.28 and won't be supported anymore in 2.0. + * @expectedDeprecation Calling "empty" on template "index1.twig" from template "index.twig" is deprecated since version 1.28 and won't be supported anymore in 2.0. + * @expectedDeprecation Calling "empty" on template "index1.twig" from template "index.twig" is deprecated since version 1.28 and won't be supported anymore in 2.0. + * @expectedDeprecation Calling "renderBlock" on template "index.twig" from template "index.twig" is deprecated since version 1.28 and won't be supported anymore in 2.0. Use block("name") instead). + * @expectedDeprecation Calling "displayBlock" on template "index.twig" from template "index.twig" is deprecated since version 1.28 and won't be supported anymore in 2.0. Use block("name") instead). + * @expectedDeprecation Calling "hasBlock" on template "index.twig" from template "index.twig" is deprecated since version 1.28 and won't be supported anymore in 2.0. Use "block("name") is defined" instead). + * @expectedDeprecation Calling "render" on template "index.twig" from template "index.twig" is deprecated since version 1.28 and won't be supported anymore in 2.0. Use include("index.twig") instead). + * @expectedDeprecation Calling "display" on template "index.twig" from template "index.twig" is deprecated since version 1.28 and won't be supported anymore in 2.0. Use include("index.twig") instead). + * @expectedDeprecation Calling "renderBlock" on template "index1.twig" from template "index.twig" is deprecated since version 1.28 and won't be supported anymore in 2.0. Use block("name", template) instead). + * @expectedDeprecation Calling "displayBlock" on template "index1.twig" from template "index.twig" is deprecated since version 1.28 and won't be supported anymore in 2.0. Use block("name", template) instead). + * @expectedDeprecation Calling "hasBlock" on template "index1.twig" from template "index.twig" is deprecated since version 1.28 and won't be supported anymore in 2.0. Use "block("name", template) is defined" instead). + * @expectedDeprecation Calling "render" on template "index1.twig" from template "index.twig" is deprecated since version 1.28 and won't be supported anymore in 2.0. Use include("index1.twig") instead). + * @expectedDeprecation Calling "display" on template "index1.twig" from template "index.twig" is deprecated since version 1.28 and won't be supported anymore in 2.0. Use include("index1.twig") instead). + */ + public function testGetAttributeWithTemplateAsObjectForDeprecations() { - $bools = array( - array(false), - ); + // to be removed in 2.0 + $twig = new Twig_Environment($this->getMockBuilder('Twig_TemplateTestLoaderInterface')->getMock()); + //$twig = new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface', 'Twig_SourceContextLoaderInterface')->getMock()); - if (function_exists('twig_template_get_attributes')) { - $bools[] = array(true); - } + $template = new Twig_TemplateTest($twig, 'index.twig'); + $template1 = new Twig_TemplateTest($twig, 'index1.twig'); - return $bools; + $this->assertInstanceof('Twig_Markup', $template->getAttribute($template1, 'string')); + $this->assertEquals('some_string', $template->getAttribute($template1, 'string')); + + $this->assertInstanceof('Twig_Markup', $template->getAttribute($template1, 'true')); + $this->assertEquals('1', $template->getAttribute($template1, 'true')); + + $this->assertInstanceof('Twig_Markup', $template->getAttribute($template1, 'zero')); + $this->assertEquals('0', $template->getAttribute($template1, 'zero')); + + $this->assertNotInstanceof('Twig_Markup', $template->getAttribute($template1, 'empty')); + $this->assertSame('', $template->getAttribute($template1, 'empty')); + + $blocks = array('name' => array($template1, 'block_name')); + + // trigger some deprecation notice messages to check them with @expectedDeprecation + $template->getAttribute($template, 'renderBlock', array('name', array(), $blocks)); + $template->getAttribute($template, 'displayBlock', array('name', array(), $blocks)); + $template->getAttribute($template, 'hasBlock', array('name', array())); + $template->getAttribute($template, 'render', array(array())); + $template->getAttribute($template, 'display', array(array())); + + $template->getAttribute($template1, 'renderBlock', array('name', array(), $blocks)); + $template->getAttribute($template1, 'displayBlock', array('name', array(), $blocks)); + $template->getAttribute($template1, 'hasBlock', array('name', array())); + $template->getAttribute($template1, 'render', array(array())); + $template->getAttribute($template1, 'display', array(array())); + + $this->assertFalse($template->getAttribute($template1, 'env', array(), Twig_Template::ANY_CALL, true)); + $this->assertFalse($template->getAttribute($template1, 'environment', array(), Twig_Template::ANY_CALL, true)); + $this->assertFalse($template->getAttribute($template1, 'getEnvironment', array(), Twig_Template::METHOD_CALL, true)); + $this->assertFalse($template->getAttribute($template1, 'displayWithErrorHandling', array(), Twig_Template::METHOD_CALL, true)); } /** - * @dataProvider getTestsDependingOnExtensionAvailability + * @group legacy + * @expectedDeprecation Silent display of undefined block "unknown" in template "index.twig" is deprecated since version 1.29 and will throw an exception in 2.0. Use the "block('unknown') is defined" expression to test for block existence. + * @expectedDeprecation Silent display of undefined block "unknown" in template "index.twig" is deprecated since version 1.29 and will throw an exception in 2.0. Use the "block('unknown') is defined" expression to test for block existence. */ - public function testGetAttributeOnArrayWithConfusableKey($useExt = false) + public function testRenderBlockWithUndefinedBlock() { - $template = new Twig_TemplateTest( - new Twig_Environment($this->getMock('Twig_LoaderInterface')), - $useExt - ); + $twig = new Twig_Environment($this->getMockBuilder('Twig_TemplateTestLoaderInterface')->getMock()); + + $template = new Twig_TemplateTest($twig, 'index.twig'); + $template->renderBlock('unknown', array()); + $template->displayBlock('unknown', array()); + } + + public function testGetAttributeOnArrayWithConfusableKey() + { + $template = new Twig_TemplateTest(new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock())); $array = array('Zero', 'One', -1 => 'MinusOne', '' => 'EmptyString', '1.5' => 'FloatButString', '01' => 'IntegerButStringWithLeadingZeros'); @@ -198,21 +239,12 @@ class Twig_Tests_TemplateTest extends PHPUnit_Framework_TestCase $this->assertSame('EmptyString', $template->getAttribute($array, null), 'null is treated as "" when accessing an array (equals PHP behavior)'); } - public function getTestsDependingOnExtensionAvailability() - { - if (function_exists('twig_template_get_attributes')) { - return array(array(false), array(true)); - } - - return array(array(false)); - } - /** * @dataProvider getGetAttributeTests */ - public function testGetAttribute($defined, $value, $object, $item, $arguments, $type, $useExt = false) + public function testGetAttribute($defined, $value, $object, $item, $arguments, $type) { - $template = new Twig_TemplateTest(new Twig_Environment($this->getMock('Twig_LoaderInterface')), $useExt); + $template = new Twig_TemplateTest(new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock())); $this->assertEquals($value, $template->getAttribute($object, $item, $arguments, $type)); } @@ -220,31 +252,31 @@ class Twig_Tests_TemplateTest extends PHPUnit_Framework_TestCase /** * @dataProvider getGetAttributeTests */ - public function testGetAttributeStrict($defined, $value, $object, $item, $arguments, $type, $useExt = false, $exceptionMessage = null) + public function testGetAttributeStrict($defined, $value, $object, $item, $arguments, $type, $exceptionMessage = null) { - $template = new Twig_TemplateTest(new Twig_Environment($this->getMock('Twig_LoaderInterface'), array('strict_variables' => true)), $useExt); + $template = new Twig_TemplateTest(new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock(), array('strict_variables' => true))); if ($defined) { $this->assertEquals($value, $template->getAttribute($object, $item, $arguments, $type)); } else { - try { - $this->assertEquals($value, $template->getAttribute($object, $item, $arguments, $type)); - - throw new Exception('Expected Twig_Error_Runtime exception.'); - } catch (Twig_Error_Runtime $e) { + if (method_exists($this, 'expectException')) { + $this->expectException('Twig_Error_Runtime'); if (null !== $exceptionMessage) { - $this->assertSame($exceptionMessage, $e->getMessage()); + $this->expectExceptionMessage($exceptionMessage); } + } else { + $this->setExpectedException('Twig_Error_Runtime', $exceptionMessage); } + $this->assertEquals($value, $template->getAttribute($object, $item, $arguments, $type)); } } /** * @dataProvider getGetAttributeTests */ - public function testGetAttributeDefined($defined, $value, $object, $item, $arguments, $type, $useExt = false) + public function testGetAttributeDefined($defined, $value, $object, $item, $arguments, $type) { - $template = new Twig_TemplateTest(new Twig_Environment($this->getMock('Twig_LoaderInterface')), $useExt); + $template = new Twig_TemplateTest(new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock())); $this->assertEquals($defined, $template->getAttribute($object, $item, $arguments, $type, true)); } @@ -252,19 +284,16 @@ class Twig_Tests_TemplateTest extends PHPUnit_Framework_TestCase /** * @dataProvider getGetAttributeTests */ - public function testGetAttributeDefinedStrict($defined, $value, $object, $item, $arguments, $type, $useExt = false) + public function testGetAttributeDefinedStrict($defined, $value, $object, $item, $arguments, $type) { - $template = new Twig_TemplateTest(new Twig_Environment($this->getMock('Twig_LoaderInterface'), array('strict_variables' => true)), $useExt); + $template = new Twig_TemplateTest(new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock(), array('strict_variables' => true))); $this->assertEquals($defined, $template->getAttribute($object, $item, $arguments, $type, true)); } - /** - * @dataProvider getTestsDependingOnExtensionAvailability - */ - public function testGetAttributeCallExceptions($useExt = false) + public function testGetAttributeCallExceptions() { - $template = new Twig_TemplateTest(new Twig_Environment($this->getMock('Twig_LoaderInterface')), $useExt); + $template = new Twig_TemplateTest(new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock())); $object = new Twig_TemplateMagicMethodExceptionObject(); @@ -279,6 +308,7 @@ class Twig_Tests_TemplateTest extends PHPUnit_Framework_TestCase 'null' => null, '1' => 1, 'bar' => true, + 'baz' => 'baz', '09' => '09', '+4' => '+4', ); @@ -307,6 +337,7 @@ class Twig_Tests_TemplateTest extends PHPUnit_Framework_TestCase array(true, 1, 1.0), array(true, null, 'null'), array(true, true, 'bar'), + array(true, 'baz', 'baz'), array(true, '09', '09'), array(true, '+4', '+4'), ); @@ -382,39 +413,41 @@ class Twig_Tests_TemplateTest extends PHPUnit_Framework_TestCase array(false, null, $methodAndPropObject, 'c', array(), $anyType), array(false, null, $methodAndPropObject, 'c', array(), $methodType), array(false, null, $methodAndPropObject, 'c', array(), $arrayType), - )); // tests when input is not an array or object $tests = array_merge($tests, array( - array(false, null, 42, 'a', array(), $anyType, false, 'Impossible to access an attribute ("a") on a integer variable ("42")'), - array(false, null, 'string', 'a', array(), $anyType, false, 'Impossible to access an attribute ("a") on a string variable ("string")'), - array(false, null, array(), 'a', array(), $anyType, false, 'Key "a" does not exist as the array is empty'), + array(false, null, 42, 'a', array(), $anyType, 'Impossible to access an attribute ("a") on a integer variable ("42") in "index.twig".'), + array(false, null, 'string', 'a', array(), $anyType, 'Impossible to access an attribute ("a") on a string variable ("string") in "index.twig".'), + array(false, null, array(), 'a', array(), $anyType, 'Key "a" does not exist as the array is empty in "index.twig".'), )); - // add twig_template_get_attributes tests - - if (function_exists('twig_template_get_attributes')) { - foreach (array_slice($tests, 0) as $test) { - $test = array_pad($test, 7, null); - $test[6] = true; - $tests[] = $test; - } - } - return $tests; } + + /** + * @expectedException Twig_Error_Runtime + */ + public function testGetIsMethods() + { + $getIsObject = new Twig_TemplateGetIsMethods(); + $template = new Twig_TemplateTest(new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock(), array('strict_variables' => true))); + // first time should not create a cache for "get" + $this->assertNull($template->getAttribute($getIsObject, 'get')); + // 0 should be in the method cache now, so this should fail + $this->assertNull($template->getAttribute($getIsObject, 0)); + } } class Twig_TemplateTest extends Twig_Template { - protected $useExtGetAttribute = false; + private $name; - public function __construct(Twig_Environment $env, $useExtGetAttribute = false) + public function __construct(Twig_Environment $env, $name = 'index.twig') { parent::__construct($env); - $this->useExtGetAttribute = $useExtGetAttribute; self::$cache = array(); + $this->name = $name; } public function getZero() @@ -439,6 +472,7 @@ class Twig_TemplateTest extends Twig_Template public function getTemplateName() { + return $this->name; } public function getDebugInfo() @@ -448,6 +482,7 @@ class Twig_TemplateTest extends Twig_Template protected function doGetParent(array $context) { + return false; } protected function doDisplay(array $context, array $blocks = array()) @@ -456,12 +491,16 @@ class Twig_TemplateTest extends Twig_Template public function getAttribute($object, $item, array $arguments = array(), $type = Twig_Template::ANY_CALL, $isDefinedTest = false, $ignoreStrictCheck = false) { - if ($this->useExtGetAttribute) { + if (function_exists('twig_template_get_attributes')) { return twig_template_get_attributes($this, $object, $item, $arguments, $type, $isDefinedTest, $ignoreStrictCheck); } else { return parent::getAttribute($object, $item, $arguments, $type, $isDefinedTest, $ignoreStrictCheck); } } + + public function block_name($context, array $blocks = array()) + { + } } class Twig_TemplateArrayAccessObject implements ArrayAccess @@ -474,6 +513,7 @@ class Twig_TemplateArrayAccessObject implements ArrayAccess 'null' => null, '1' => 1, 'bar' => true, + 'baz' => 'baz', '09' => '09', '+4' => '+4', ); @@ -506,6 +546,7 @@ class Twig_TemplateMagicPropertyObject 'null' => null, '1' => 1, 'bar' => true, + 'baz' => 'baz', '09' => '09', '+4' => '+4', ); @@ -537,6 +578,7 @@ class Twig_TemplatePropertyObject public $zero = 0; public $null = null; public $bar = true; + public $baz = 'baz'; protected $protected = 'protected'; } @@ -551,7 +593,13 @@ class Twig_TemplatePropertyObjectAndIterator extends Twig_TemplatePropertyObject class Twig_TemplatePropertyObjectAndArrayAccess extends Twig_TemplatePropertyObject implements ArrayAccess { - private $data = array(); + private $data = array( + 'defined' => 'defined', + 'zero' => 0, + 'null' => null, + 'bar' => true, + 'baz' => 'baz', + ); public function offsetExists($offset) { @@ -613,6 +661,16 @@ class Twig_TemplateMethodObject return true; } + public function isBaz() + { + return 'should never be returned'; + } + + public function getBaz() + { + return 'baz'; + } + protected function getProtected() { return 'protected'; @@ -624,21 +682,35 @@ class Twig_TemplateMethodObject } } +class Twig_TemplateGetIsMethods +{ + public function get() + { + } + + public function is() + { + } +} + class Twig_TemplateMethodAndPropObject { private $a = 'a_prop'; + public function getA() { return 'a'; } public $b = 'b_prop'; + public function getB() { return 'b'; } private $c = 'c_prop'; + private function getC() { return 'c'; @@ -657,7 +729,7 @@ class Twig_TemplateMagicMethodExceptionObject { public function __call($method, $arguments) { - throw new BadMethodCallException(sprintf('Unkown method %s', $method)); + throw new BadMethodCallException(sprintf('Unknown method "%s".', $method)); } } @@ -682,3 +754,8 @@ class CExtDisablingNodeVisitor implements Twig_NodeVisitorInterface return 0; } } + +// to be removed in 2.0 +interface Twig_TemplateTestLoaderInterface extends Twig_LoaderInterface, Twig_SourceContextLoaderInterface +{ +} diff --git a/lib/silex/vendor/twig/twig/test/Twig/Tests/TemplateWrapperTest.php b/lib/silex/vendor/twig/twig/test/Twig/Tests/TemplateWrapperTest.php new file mode 100644 index 000000000..cc71052c4 --- /dev/null +++ b/lib/silex/vendor/twig/twig/test/Twig/Tests/TemplateWrapperTest.php @@ -0,0 +1,64 @@ + '{% block foo %}{% endblock %}', + 'index_with_use' => '{% use "imported" %}{% block foo %}{% endblock %}', + 'index_with_extends' => '{% extends "extended" %}{% block foo %}{% endblock %}', + 'imported' => '{% block imported %}{% endblock %}', + 'extended' => '{% block extended %}{% endblock %}', + ))); + + $wrapper = new Twig_TemplateWrapper($twig, $twig->loadTemplate('index')); + $this->assertTrue($wrapper->hasBlock('foo')); + $this->assertFalse($wrapper->hasBlock('bar')); + $this->assertEquals(array('foo'), $wrapper->getBlockNames()); + + $wrapper = new Twig_TemplateWrapper($twig, $twig->loadTemplate('index_with_use')); + $this->assertTrue($wrapper->hasBlock('foo')); + $this->assertTrue($wrapper->hasBlock('imported')); + $this->assertEquals(array('imported', 'foo'), $wrapper->getBlockNames()); + + $wrapper = new Twig_TemplateWrapper($twig, $twig->loadTemplate('index_with_extends')); + $this->assertTrue($wrapper->hasBlock('foo')); + $this->assertTrue($wrapper->hasBlock('extended')); + $this->assertEquals(array('foo', 'extended'), $wrapper->getBlockNames()); + } + + public function testRenderBlock() + { + $twig = new Twig_Environment(new Twig_Loader_Array(array( + 'index' => '{% block foo %}{{ foo }}{{ bar }}{% endblock %}', + ))); + $twig->addGlobal('bar', 'BAR'); + + $wrapper = new Twig_TemplateWrapper($twig, $twig->loadTemplate('index')); + $this->assertEquals('FOOBAR', $wrapper->renderBlock('foo', array('foo' => 'FOO'))); + } + + public function testDisplayBlock() + { + $twig = new Twig_Environment(new Twig_Loader_Array(array( + 'index' => '{% block foo %}{{ foo }}{{ bar }}{% endblock %}', + ))); + $twig->addGlobal('bar', 'BAR'); + + $wrapper = new Twig_TemplateWrapper($twig, $twig->loadTemplate('index')); + + ob_start(); + $wrapper->displayBlock('foo', array('foo' => 'FOO')); + + $this->assertEquals('FOOBAR', ob_get_clean()); + } +} diff --git a/lib/silex/vendor/twig/twig/test/Twig/Tests/TokenStreamTest.php b/lib/silex/vendor/twig/twig/test/Twig/Tests/TokenStreamTest.php index fd4ec6337..f219c9554 100644 --- a/lib/silex/vendor/twig/twig/test/Twig/Tests/TokenStreamTest.php +++ b/lib/silex/vendor/twig/twig/test/Twig/Tests/TokenStreamTest.php @@ -9,11 +9,11 @@ * file that was distributed with this source code. */ -class Twig_Tests_TokenStreamTest extends PHPUnit_Framework_TestCase +class Twig_Tests_TokenStreamTest extends \PHPUnit\Framework\TestCase { protected static $tokens; - public function setUp() + protected function setUp() { self::$tokens = array( new Twig_Token(Twig_Token::TEXT_TYPE, 1, 1), @@ -27,6 +27,18 @@ class Twig_Tests_TokenStreamTest extends PHPUnit_Framework_TestCase ); } + /** + * @group legacy + */ + public function testLegacyConstructorSignature() + { + $stream = new Twig_TokenStream(array(), 'foo', '{{ foo }}'); + $this->assertEquals('foo', $stream->getFilename()); + $this->assertEquals('{{ foo }}', $stream->getSource()); + $this->assertEquals('foo', $stream->getSourceContext()->getName()); + $this->assertEquals('{{ foo }}', $stream->getSourceContext()->getCode()); + } + public function testNext() { $stream = new Twig_TokenStream(self::$tokens); @@ -40,8 +52,8 @@ class Twig_Tests_TokenStreamTest extends PHPUnit_Framework_TestCase } /** - * @expectedException Twig_Error_Syntax - * @expectedMessage Unexpected end of template + * @expectedException Twig_Error_Syntax + * @expectedExceptionMessage Unexpected end of template */ public function testEndOfTemplateNext() { @@ -54,8 +66,8 @@ class Twig_Tests_TokenStreamTest extends PHPUnit_Framework_TestCase } /** - * @expectedException Twig_Error_Syntax - * @expectedMessage Unexpected end of template + * @expectedException Twig_Error_Syntax + * @expectedExceptionMessage Unexpected end of template */ public function testEndOfTemplateLook() { diff --git a/lib/silex/vendor/twig/twig/test/Twig/Tests/Util/DeprecationCollectorTest.php b/lib/silex/vendor/twig/twig/test/Twig/Tests/Util/DeprecationCollectorTest.php new file mode 100644 index 000000000..887a90afa --- /dev/null +++ b/lib/silex/vendor/twig/twig/test/Twig/Tests/Util/DeprecationCollectorTest.php @@ -0,0 +1,42 @@ +getMockBuilder('Twig_LoaderInterface')->getMock()); + $twig->addFunction(new Twig_SimpleFunction('deprec', array($this, 'deprec'), array('deprecated' => true))); + + $collector = new Twig_Util_DeprecationCollector($twig); + $deprecations = $collector->collect(new Twig_Tests_Util_Iterator()); + + $this->assertEquals(array('Twig Function "deprec" is deprecated in deprec.twig at line 1.'), $deprecations); + } + + public function deprec() + { + } +} + +class Twig_Tests_Util_Iterator implements IteratorAggregate +{ + public function getIterator() + { + return new ArrayIterator(array( + 'ok.twig' => '{{ foo }}', + 'deprec.twig' => '{{ deprec("foo") }}', + )); + } +} diff --git a/lib/silex/vendor/twig/twig/test/Twig/Tests/escapingTest.php b/lib/silex/vendor/twig/twig/test/Twig/Tests/escapingTest.php index 7b765caee..9b98dddcf 100644 --- a/lib/silex/vendor/twig/twig/test/Twig/Tests/escapingTest.php +++ b/lib/silex/vendor/twig/twig/test/Twig/Tests/escapingTest.php @@ -6,7 +6,7 @@ * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com) * @license http://framework.zend.com/license/new-bsd New BSD License */ -class Twig_Test_EscapingTest extends PHPUnit_Framework_TestCase +class Twig_Test_EscapingTest extends \PHPUnit\Framework\TestCase { /** * All character encodings supported by htmlspecialchars(). @@ -144,9 +144,9 @@ class Twig_Test_EscapingTest extends PHPUnit_Framework_TestCase protected $env; - public function setUp() + protected function setUp() { - $this->env = new Twig_Environment($this->getMock('Twig_LoaderInterface')); + $this->env = new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock()); } public function testHtmlEscapingConvertsSpecialChars() @@ -250,7 +250,7 @@ class Twig_Test_EscapingTest extends PHPUnit_Framework_TestCase .chr($codepoint >> 6 & 0x3f | 0x80) .chr($codepoint & 0x3f | 0x80); } - throw new Exception('Codepoint requested outside of Unicode range'); + throw new Exception('Codepoint requested outside of Unicode range.'); } public function testJavascriptEscapingEscapesOwaspRecommendedRanges()